# Javatest Anleitung

Aufbau | Übersicht der Befehle | Konstanten und Variablen | Befehle im Detail | Reguläre Ausdrücke

## Aufbau

Javatest-Script.

- Zeilen, die mit `#` beginnen, sind Befehle. Diese müssen GROSS geschrieben sein.
- Zeilen, die mit `*` oder `+` beginnen, werden direkt als HIT-Request gesendet.
- Zeilen, die mit `;` beginnen oder komplett leer sind, werden ignoriert.
- alle anderen Zeilen führen zu einem Fehler

Vor jeder Ausführung eines Befehls oder Requests werden Konstanten und Variablen ersetzt.

Tritt irgend ein Fehler auf, beendet sich das Script.

## Befehlsübersicht

Kategorie | Befehle
--- | ---
Ablaufsteuerung | `#ECHO`, `#PRINT`, `#PRINT_LINE`, `#LINE_NUMBERS`, `#PRINT_MEM_USAGE` `#PAUSE`, `#SLEEP`, `#STOP`, `#PANIK`, `#EXIT`, `#LABEL`, `#GOTOLABEL`, `#SKIP`, `#CONTINUE_HERE`, `#INCLUDE` `#DEFINE`, `#UNDEF`, `#IFDEF`, `#ELSEIFDEF`, `#IF`, `#ELSEIF`, `#ELSE`, `#ENDIF` `#FORALL` (= `#FORALL_VAR`), `#FOR` (= `#FORALL_STEPS`), `#FORALL_HIT`, `#FORALL_FILE`, `#FORALL_SELECT`, `#FORALL_DIRS`, `#FORALL_ARRAY`, `#FORALL_LIST`, `#NEXT` (= `#ENDFORALL`)
Variablen | `#SETVAR`, `#REMVAR`, `#UPDATEVAR`, `#CONCATVAR`, `#SETVAR_PLAUSIWERT`, `#PRINT_VARS`, `#IFVAR` `#START_LOM`, `#NEXT_LOM`, `#SET_LOM_RANGE`, `#SET_BNR_RANGE`, `#HITP_QUOTE`, `#HITP_UNQUOTE` `#SETHITVAR`, `#REMHITVAR` `#ARRAY_CREATE`, `#ARRAY_ADD_HIT`, `#ARRAY_LOAD`, `#ARRAY_SAVE`, `#PRINT_ARRAY`, `#ARRAY_ROWS_BW`, `#ARRAY_ROWS_EQ` `#SUBST_CREATE_TEXT`, `#SUBST_CREATE_BNR`, `#SUBST_CREATE_LOM`, `#SUBST_CREATE_EMAIL`, `#SUBST_CREATE_TEXTUUID`, `#SUBST_REPLACE`, `#SUBST_REVERSE`, `#SUBST_LOAD`, `#SUBST_SAVE`, `#PRINT_SUBST` `#SET_BLOBMARKER`, `#SET_BLOB_TEXTUUID`
HIT-Protokoll | `#ALWAYS_BW`, `#SCHWERE_EQ`, `#SCHWERE_BW` `#ZEILEN_ZAHL_BW` `#PLAUSI_COUNT`, `#PLAUSI_EXIST_ALL`, `#PLAUSI_EXIST_ONE`, `#PLAUSI_NOT_EXIST`, `#PLAUSI_EXIST_ONLY`, `#ALWAYS_IGNORE_PLAUSI` `#PRINT_STATUS`, `#PRINT_DATEN`, `#PRINT_DATENX`, `#WRITE_DATEN` `#SPALTE_MIT_TEXT`, `#SPALTE_OHNE_TEXT`, `#HEAD_MIT_TEXT`, `#HEAD_OHNE_TEXT`, `#ZEILE_MIT_TEXT`, `#ZEILE_OHNE_TEXT`, ``#ZEI_N_MIT_TEXT``, `#ZEI_N_OHNE_TEXT` `#SPALTE_MIT_REGEX`, `#SPALTE_OHNE_REGEX`, `#ZEILE_MIT_REGEX`, `#ZEILE_OHNE_REGEX`, `#ZEI_N_MIT_REGEX`, `#ZEI_N_OHNE_REGEX` `#REGEX_EXACT_MATCH` `#IS_VALID_LOM, #IS_INVALID_LOM,` `#IS_VALID_SW_LOMB, #IS_INVALID_SW_LOMB, #IS_VALID_SZ_LOMB, #IS_INVALID_SZ_LOMB, #IS_VALID_SZ_LOMS, #IS_INVALID_SZ_LOMS` `#PLAUSI_MIT_TEXT`, `#PLAUSI_OHNE_TEXT`, `#PLS_N_MIT_TEXT`, `#PLS_N_OHNE_TEXT`, `#PLAUSI_MIT_REGEX`, `#PLAUSI_OHNE_REGEX`, `#PLS_N_MIT_REGEX`, `#PLS_N_OHNE_REGEX` `#RECONNECT`, `#SET_HITP_SERVER` `#CSV_UPLOAD`, `#HITBATCH`
SQL | `#EXEC_SQL`, `#SET_AUTOCOMMIT`, `#INSERT_PIN_SYS`, `#INSERT_PIN_SELF`, `#CHECK_PIN_INTEGRITY`
Aposti | `#TEST_LOM`, `#PRINT_VVVO_VORGAENGE`, `#PRINT_VET_VORGAENGE`, `#PRINT_LEBENSLAUF` `#HAS_VVVO_VORGANG`, `#HAS_VVVO_ZOMBIES`, `#HAS_VET_VORGANG`, `#APOSTI_PLAUSIS_IN_DB`
Web | `#FLUSH_COOKIES`, `#PRINT_COOKIES`, `#USE_PROXY` `#HTTP_GET`, `#HTTP_POST`, `#FORM_NEW`, `#FORM_ADD`, `#FORM_SET`, `#FORM_SET_FILE`, `#FORM_FROM_HTML` `#PRINT_HTTP_CONTENT`, `#PRINT_HTTP_HEADERS`, `#PRINT_HTTP_STATS`, `#PRINT_FORM_DATA` `#TEST_REDIR_URL`, `#TEST_HTTP_STATUS`, `#HAS_REDIRS` `#HTTP_FIND_START`, `#HTTP_FIND_TEXT`, `#HTTP_FIND_OHNE_TEXT`
Zeitmessung | `#STOPWATCH_START` , `#STOPWATCH_GET` , `#STOPWATCH_RESET` , `#STOPWATCH_STOP` `#STOPWATCH_ASSERT` `#GET_TIMESTAMP`
Dateizugriff | `#SET_OUTFILE`, `#WRITE_OUTFILE`, `#WRITE_DATEN`, `#FILE_COMP`, `#FILE_PRINT`
Sonstiges | `#SET_LOGFILEMAX`, `#SET_END_SEMIKOLON`

## Konstanten und Variablen

### Konstanten

Konstanten werden in `%` eingeschlossen. Diese enthalten einen festen Wert, der entweder vom Javatest vorgegeben ist oder zu Beginn des Scripts festgelegt wird.

Folgende Konstanten sind möglich:

Konstante | Wert
--- | ---
`%Heute%` | das Datum von heute im Format `TT.MM.JJJJ`
`%Morgen%` | das Datum von morgen im Format `TT.MM.JJJJ`
`%Gestern%` | das Datum von gestern im Format `TT.MM.JJJJ`
`%Vorgestern%` | das Datum von vorgestern im Format `TT.MM.JJJJ`
`%HeuteX%` | das Datum von heute im Format `yyyy-mm-dd`
`%MorgenX%` | das Datum von morgen im Format `yyyy-mm-dd`
`%GesternX%` | das Datum von gestern im Format `yyyy-mm-dd`
`%VorgesternX%` | das Datum von vorgestern im Format `yyyy-mm-dd`
`%Jetzt%` | den aktuellen Zeitpunkt im Format `dd.MM.yyyy HH:mm:ss.SSS`
`%VorMeldefrist%` | das Datum im Format `TT.MM.JJJJ`, vor dem die Meldefrist für Aposti überschritten ist
`%HeuteMin<x>Tage%` | Heute minus `<x>` Tage im Format `TT.MM.JJJJ`
`%HeuteMin<x>MonateRoundDown%` | Heute minus `<x>` Monate im Format `TT.MM.JJJJ`, wobei beim Abrunden "überstehende" Tage "abgeschnitten" werden
`%Heute<tage>T<monate>M<jahre>J%` | Dynamische Berechnung des Datums relativ zu Heute und im Format `TT.MM.JJJJ`. Für die Parameter `<tage>`, `<monate>` und `<jahre>` kann jede negative und positive Zahl sowie die `0` verwendet werden. Wichtig: Kein Leerzeichen zwischen den `%%`!
`%MemUsed%` | Anzahl Bytes, die das Javatest-Programm im gerade aktuellen `TestCase` belegt
`%ENV:name%` | Übernimmt den Wert aus den Umgebungsvariablen des Betriebssystems, die beim Start des Javatest gesetzt waren. Es sind alle Namen links des `=` möglich, die unter `cmd.exe` mit dem Befehl `set` aufgelistet werden. Groß-/Kleinschreibung wird hier beim Konstantennamen ausnahmsweise nicht berücksichtigt.
`%GUID%` | Liefert bei jeder(!) Verwendung einen neuen Globally Unique Identifier, kurz GUID. Kann verwendet werden, um in bestimmten Tabellen einen neuen eindeutigen Datensatz anzulegen. Wichtig dabei ist immer, dass die Integritätsbedingungen der Primary Keys (PKs) weiterhin beachtet werden müssen. Zwei Datensätze mit identischen PKs können nicht unter unterschiedlichen GUIDs abgelegt werden. Die GUID kann natürlich auch für andere Zwecke verwendet werden.
`%Kw%` | aktuelle Kalenderwoche im Format `NN`
`%KwJ%` | das Jahr der Kalenderwoche (kann von Heuer abweichen) im Format `NNNN`
`%WoT%` | Nummer des Wochentags der Kalenderwoche im Format `N`
`%TAM_P_AKT%` | aktuelle TAM-Periode im Format `NNN`
`%TAM_P_NEXT%` | vorhergehende TAM-Periode im Format `NNN`
`%TAM_P_NEXT%` | folgende TAM-Periode im Format `NNN`
`%TAM_P_ANFANG%` | Beginn-Datum der aktuellen TAM-Periode im Format `TT.MM.JJJJ`
`%TAM_P_ENDE%` | Ende-Datum der aktuellen TAM-Periode im Format `TT.MM.JJJJ`

Die Groß- und Kleinschreibung wird berücksichtigt. Sollte am Ende des Ersetzungsvorgangs eine Konstante mit unbekanntem Namen stehen bleiben, dann wird nichts ersetzt und die Konstante bleibt mit den umschließenden Prozentzeichen stehen.

### Variablen

Variablen werden in `$` eingeschlossen. Diese können durch die Befehle `#SETVAR`, `#UPDATEVAR` und `#REMVAR` gesetzt und gelöscht werden. Auf definierte Variablen wird dann durch `$<variable>$` zugegriffen. Die Groß- und Kleinschreibung der Variablennamen wird berücksichtigt, d.h. `$hit$` und `$Hit$` und `$HIT$` sind drei verschiedene Variablen. Eine nicht definierte Variable wird nicht ersetzt und führt zu keinem Fehler (da das HIT-Protokoll das Zeichen `$` auch verwendet), z.B.:

`#SETVAR Foo Bar
#PRINT $Foo$ zahlen bitte!
#``PRINT` `Geldbeutel: $leer$`

liefert im Logfile

`Bar zahlen bitte
Geldbeutel: $leer$`

Einzelne Befehle setzen definierte Variablen:

Variable | Wert
--- | ---
`$LOM$` | wird von `#START_LOM` und `#NEXT_LOM` gesetzt
`$SqlBnrRange$` | wird von `#SET_BNR_RANGE` gesetzt
`$SqlLomRange$` | wird von `#SET_LOM_RANGE` gesetzt

Um das Zeichen `$` selbst zu verarbeiten, stellt man ihm ein Backslash `\` vor (das erste `$` einer Deklaration reicht):

`#SETVAR Foo Bar
#PRINT $Foo$ zahlen bitte, \$Foo$!`

liefert im Logfile

`Bar zahlen bitte, $Foo$!`

Neben diesen Variablen, die mit Zeichenkettenwerten arbeiten, existieren noch weitere Typen:

- Arrays
- Substitutoren

#### Arrays

Arrays speichern Spalten und Zeilen, die entweder aus einer HIT-Abfrage (mit `#ARRAY_ADD_HIT`) oder einer CSV-Datei (mit `#ARRAY_LOAD`) eingelesen werden können. Speichern in einer CSV-Datei (mit `#ARRAY_SAVE`) ist auch möglich. Zusätzlich erlauben Arrays das Mitführen von Datentypen je Spalte, sofern sie bekannt sind - standardmäßig sind es Zeichenketten. Ein Array als Ganzes wird als Variable unter einem Namen `<name>` gesetzt. Der Name dient dabei als Verweis auf das Array und muss somit bei allen Array-Befehlen angegeben werden.

Wenn im Array Daten hinterlegt sind, dann können diese als Variable `$<name>[zeile][spalte]$` jederzeit verwendet werden:

    #ARRAY_LOAD demo test.csv
    #PRINT "$demo[1][1]$" steht in der erste Zeile und ersten Spalte.
    #PRINT "$demo[1][BNR15]$" auch.

`zeile` und `spalte` sind 1-basierte Indexe, zudem darf `spalte` auch eine benannte Spaltenbezeichnung sein.

Neben den Elementzugriffen existieren folgende Sonderindexe:

> Zeilen- Index | Spalten- Index | wird ersetzt durch | Beispiel | liefert z.B.
> --- | --- | --- | --- | ---
> ohne | ohne | nichts! `$$`-Ausdruck bleibt stehen | `$demo$` | `$demo$`
> `#` | ohne | Anzahl Datenzeilen im Array | `$demo[#]$` | `5`
> `#` | `#` | Anzahl Spalten im Array | `$demo[#][#]$` | `3`
> `0` | ohne | CSV-String der Spaltennamen | `$demo[0]$` | `BNR15;NAME;SYS_VON`
> `0` | `spalte` | Spaltenname der gegebenen Spalte | `$demo[0][3]$` | `SYS_VON`
> `1`..`n` | ohne | CSV-String der Spaltendaten | `$demo[3]$` | `276090000000001;Name-1;...`

Alle anderen Varianten und Inhalte liefern nichts, d.h. der Variablenausdruck bleibt unverändert stehen.

Das komplette Array kann leicht so im Logfile ausgegeben werden:

    #FORALL_ARRAY demo row
       #PRINT $row$
    #NEXT

Wichtig: mit `#REMVAR` und anderen Variablenbefehlen kann nur `<name>` bearbeitet werden, jedoch nicht eine konkrete Spalte in einer konkreten Zeile!

Je nach Anwendungsfall ist zu empfehlen, nach Gebrauch mit einem `#REMVAR <name>` ein Array komplett freizugeben, da sich sonst der Speicherverbrauch drastisch erhöht.

#### Substitutoren

Ein Substitutor hat die Aufgabe, Ersetzungen vorzunehmen. Jeder dieser führt intern eine Übersetzungstabelle mit, die wie folgt gefüllt wird:

- existiert zu ersetzender Wert nicht in der Tabelle, wird nach einer gewissen Vorschrift ein neuer Ersetzungswert erzeugt und mit dem zu ersetzenden Wert in der Tabelle vermerkt
- existiert er schon, wird einfach dessen Ersetzungswert zurückgegeben

Ein Substitutor wird analog den Arrays als Ganzes unter einem Namen `<name>` als Variable gesetzt. Der Name dient dabei als Verweis auf den Substitutor und muss somit bei allen Substitutor-Befehlen angegeben werden.

Die Ersetzung eines Wertes kann auf zwei Arten vorgenommen werden:

1. mit `#SUBST_REPLACE` den Inhalt einer bereits gesetzten Variable durchdessen Ersetzungswert ersetzen
2. mit dem Variablenausdruck `$<name>[<variable>]$`. Die Variable `<variable>` muss den zu ersetzenden Wert enthalten, der -Vorteil gegenüber 1.- in der Variable nicht verändert wird.

Auch Substitutoren konnen gesichert (mit `#SUBST_SAVE`) und geladen (mit `#SUBST_LOAD`) werden. `#SUBST_LOAD` erkennt anhand separat gespeicherter Metadaten, um welchen Substitutor es sich handelt und erzeugt und initialisiert diesen mit genau diesen Daten.

Beispiel:

    #SUBST_CREATE_TEXT textSubst 0 0 16
    #SETVAR t irgendwas
    #PRINT alt: $t$ -> neu: $textSubst[t]$
    #SUBST_REPLACE textSubst t

Zeile 1: `#SUBST_CREATE_TEXT` definiert einen Text-Substitutor (erzeugt zufällige Zeichenketten) mit dem Namen `textSubst`.
Zeile 2 setzt Variable `t` auf einen Wert, der durch eine zufällige Zeichenkette ersetzt werden soll.
Zeile 3 gibt einen Text aus, der `t` im Originalzustand und als Ersetzung anhand des Substitutors ausgibt. Der Name `textSubst` gibt an, welcher Substitutor es ist und der Variablenname `t` als Parameter gibt an, welcher Wert übersetzt werden soll. Die Variable (hier `t`) wird hierbei inhaltlich nicht verändert.
Zeile 4 führt die identische Ersetzung durch, setzt jedoch den ersetzten Wert in der Variable `t`.

Das Beispiel gibt dann z.B. dies aus:

    alt: irgendwas -> neu: XXXxXXxxxxxxxxx

Bei Listen- oder Arrayverarbeitung läßt sich die Variablenersetzung ebenfalls verwenden:

    ; gib aus dem Array "betriebe" den Inhalt der Spalte "NAME" der Zeile "3" aus
    #PRINT NAME der 3ten Zeile: $betriebe[3,NAME]$.
    ; Ersetzen und ausgeben (anhand obigem Substitutor):
    #PRINT ersetzter NAME der 3ten Zeile: $textSubst[betriebe,3,NAME]$.

Beginnt der Variablenausdruck mit einem Substitutor (2tes `#PRINT`), dann folgt im Klammerausdruck eine Verweis-Liste, die ausgehend von einer Liste bzw. einem Array bis hin zum einzelnen Element, das ersetzt werden soll. Hier ist es die Spalte `NAME` der `3`-ten Zeile des Arrays `betriebe`.

Eine Rückübersetzung ist auch möglich mit `#SUBST_REVERSE` (kein entsprechender Variablenausdruck vorhanden).

Je nach Anwendungsfall ist zu empfehlen, nach Gebrauch mit einem `#REMVAR <name>` einen Substitutor komplett freizugeben, da sich sonst der Speicherverbrauch drastisch erhöht.

## Die Befehle im Detail

Die Syntax der Befehle wird wie folgt beschrieben:

- In `<>` eingeschlossen sind Pflichtparameter,
- in `[]` eingeschlossen optionale Parameter,
- `...` bedeutet Wiederholung des links stehenden Parameters,
- sonstiger Text muss so wie angegeben verwendet werden.

### Ablaufsteuerung

`#ECHO <modus>`

> Legt fest, wieviel protokolliert werden soll. Es gibt folgende Modi:

> Modus | Bedeutung
> --- | ---
> `-1` | Absolut nichts ausgeben. Selbst alle expliziten `#PRINT`-Befehle schreiben nichts ins Logfile.
> `0` | Fast nichts ausgeben (ist Default beim Start jedes Scripts). Nur Ausgabebefehle wie `#PRINT` oder Zustandshinweise von bestimmten Befehlen werden ausgegeben.
> `1` | zusätzlich zu Modus `0` wird die Befehlszeile ausgegeben
> `2` | zusätzlich zu Modus `1` wird der HIT-Status ausgegeben
> `3` | zusätzlich zu Modus `2` werden die HIT-Antworten ausgegeben

> Generell werden Im Logfile wird bei Modi `1`-`3` automatisch die Zeilennummer bei der Befehlszeile mitprotokolliert. Da bei Modus `0` keine Befehlszeile ausgegeben wird, wird auch hier zusätzlich diese bei der Ausgabeergebnisse der Befehle mit ausgegeben.

`#LINE_NUMBERS [0/1]`

> Schaltet die Ausgabe der Zeilennummern im Logfile ein- bzw. aus. Default: `1`

`#PRINT [text]
#PRINT_LINE [text]`

> Gibt den angegebenen Text `<text>` aus. Ist kein Text vorhanden, dann nur eine leere Zeile.

`#PAUSE`

> Wartet auf einen Tastendruck, bevor im Script fortgefahren wird

`#SLEEP <sekunden>`

> Wartet `<sekunden>` Sekunden, bevor im Script fortgesetzt wird. Es ist maximal eine Stunde (=3600 Sekunden) möglich.

`#STOP
` `#PANIK
` `#EXIT`

> beendet das Script sofort

`#LABEL``<name>`
`#GOTOLABEL``<name>`

> `#GOTOLABEL` springt zur Zeile mit dem angegebenen `<name>`., die später im Script als `#LABEL <name>` definiert werden. Es sind nur Vorwärts-Sprünge erlaubt.
> Ein vorwärts nicht gefundenes Label führt zu einem Fehler.

> Die früher existente Funktion `#GOTOLINE` wurde deaktiviert und liefert nun einen Fehler plus einen "Ersatzhinweis". Als Ersatz kann z.B. `#GOTOLABEL zeile<zeile>` und vor (nicht statt!) der Zeile selbst `#LABEL zeile<zeile>` verwendet werden. Besser sind bzgl. der Lesbarkeit natürlich aussagekräftigere Namen.

> Innerhalb von Schleifen, wie z.B. zwischen `#FORALL` und `#ENDFORALL`, sind beide Befehle nicht zulässig und führt zu einem Fehler.

`#SKIP
` `#CONTINUE_HERE`

> `#SKIP` überspringt alle folgenden Zeilen bis zum `#CONTINUE_HERE`. Wird bis zum Ende des Scripts kein `#CONTINUE_HERE` gefunden, beendet sich das Script. Überspringt nur vorwärts.

> Innerhalb einer `#FORALL` und `#ENDFORALL` Schleife ist `#SKIP` nicht zulässig und führt zu einem Fehler.

`#INCLUDE``<dateiname>`

> Liest die Datei `<dateiname>` ein und verarbeitet sie wie jedes andere Javatest-Script. Es übernimmt sämtliche Konstanten und Variablen des aufrufenden Scripts.

> Der Dateiname `<dateiname>` kann auch relativ sein: der Dateiname bezieht sich dann auf das Verzeichnis des aufrufenden Scripts.

> Kann die Datei nicht gelesen werden, bricht der Javatest ab. Ebenso, wenn eine Datei zum zweiten Mal gleichzeitig verarbeitet wird, da sonst eine Endlosschleife auftreten würde.

`#PRINT_MEM_USAGE`

> Gibt den Speicherverbrauch der Java-VM ins Logfile aus: aktueller Verbrauch, verfügbarer Speicher und die max. Heap Space size.

`#DEFINE <key>
#UNDEF <key>
#IFDEF <key>
#ELSEIFDEF <key>
#ELSE
#ENDIF`

> Bedingtes Ausführen von Blocken anhand von Schlüsselwörtern analog der Programmiersprache C.

> Ein `#DEFINE` legt ein Schlüsselwort fest, ein `#UNDEF` entfernt eines - beide Befehle erwarten ein Schlüsselwort `<key>` als Parameter.

> Schlüsselwörter können dann mit einem `#IFDEF <key>` abgefragt werden und ist das Schlüsselwort definiert, dann werden alle Befehle bis zum nächsten `#ELSE` bzw. `#ENDIF` ausgeführt. Analog dazu überspringt Javatest Befehle wegen nicht erkannter Schlüsselwörter ebenso bis zum nächsten `#ELSEIFDEF`, `#ELSE` bzw. `#ENDIF`. Bei einem nicht vorhandenem Schlüsselwort bei `#IFDEF` wird der `#ELSE`-Block, sofern vorhanden, bis zum `#ENDIF` ausgeführt.

> `#IFDEF`...`#ELSEIFDEF`...`#ELSE`...`#ENDIF`-Blöcke können verschachtelt werden. Schleifendurchläufe u.ä. werden ignoriert, d.h. man muss selbst dafür Sorge tragen, dass Schleifen-Enden nicht öfters aufgerufen werden als Schleifen-Starts.

> Ein `#IFNDEF` lässt sich durch ein `#IFDEF` und `#ELSE` ohne Befehle dazwischen nachbilden, daher nicht implementiert.

> Die verwendeten Schlüsselwörter sind weder als Variablen, noch als Konstanten verwendbar. Sie werden in einer eigenen Symboltabelle gespeichert, die nur zum Preprocessing verwendet wird.

### Variablen

`#SETVAR``<name> [wert]`

> Setzt eine Variable `<name>` auf den Wert `<wert>`. Der Wert kann weggelassen werden, so dass eine leere Zeichenkette zugewiesen wird.

> Beginnt der Wert `<wert>` mit einem `@`, dann wird der Rest hinter dem `@` als Name einer Datei aufgefasst, aus der der Wert der Variable `<name>` gelesen wird. Z.B.

>     #SETVAR AUS_DATEI @C:\javatest_variable.txt
>     #PRINT Dateidaten: $AUS_DATEI$

> Beginnt der Wert `<wert>` mit einem `#`, dann wird der Rest hinter dem `#` als Name einer Datei aufgefasst, aus der der verschlüsselte Wert der Variable `<name>` gelesen wird. Insbesondere für PINs o.ä. geeignet. ACHTUNG: Lässt sich der verschlüsselte Inhalt der Datei nicht decodieren, dann wird dieser codiert und in die Datei zurückgeschrieben!

`#REMVAR``<name>`

> Löscht die angegebene Variable `<name>`. Danach findet keine Ersetzung mehr statt.

`#UPDATEVAR``<name> [wert]`

> Setzt eine Variable `<name>` auf den Wert `<wert>`., wenn die Variable `<name>` bereits gesetzt ist. Ist die Variable `<name>` noch nicht verwendet oder mit `#REMVAR` gelöscht worden, dann wird der Wert `<wert>` nicht zugewiesen und die Variable `<name>` bleibt gelöscht. Der Wert kann weggelassen werden, so dass eine leere Zeichenkette zugewiesen wird.

> Für Werte `<wert>`, die mit `@` oder `#` beginnen, gelten die gleichen Regeln wie bei `#SETVAR`.

`#CONCATVAR``<name> <begrenzer> [wert]`

> Erweitert die Variable `<name>` mit dem Begrenzer `<begrenzer>` um den Wert `[wert]` (wird `[wert]` nicht angegeben, wird eine leere Zeichenkette `""` angenommen)

> Damit lassen sich Zeichenketten, wie z.B. CSV-Zeichenketten leicht bauen. Der Begrenzer `<begrenzer>` wird nur dann an den Wert von `<name>` angehängt, wenn die Variable `<name>` noch nicht gesetzt war. Der Begrenzer darf leer sein, muss aber angegeben werden (mit `""`).

> Beispiel:

>     #SETVAR autos Audi;BMW;Opel;VW
>     #REMVAR alle
>     #FORALL autos i
>        #CONCATVAR alle " + " $i$
>     #ENDFORALL
>     #PRINT alle zusammen: $alle$

> Dies arbeitet die Liste der Automarken ab und erzeugt mit ihnen eine neue Variable `alle`, die dann `Audi + BMW + Opel + VW` beinhaltet. Das `#REMVAR` sorgt sicherheitshalber dafür, dass `$alle$` vor der ersten Verwendung leer ist. Vor dem ersten Wert `Audi` wird dabei kein Begrenzer gesetzt, weil `$alle$` leer war.

> Für Werte `[wert]`, die mit `@` oder `#` beginnen, gelten die gleichen Regeln wie bei `#SETVAR`.

`#SETVAR_PLAUSIWERT` `<name>` `<plausinr>`

> Suche aus der letzten HIT-Anwort die Plausinummer `<plausinr>`, extrahiere den in dessen Plausitext hinterlegten Wert und weise ihn den Variable `<name>` zu.

> Ideal für das Extrahieren der GUID nach dem Einfügen eines neuen Satzes, um diesen z.B. gleich wieder stornieren zu können:

>     *2:XS/S:ABGANG/BNR15;LOM;ABGA_DAT:11 000 000 4711;DK 37867 0902;31.02.2014
>     #SETVAR_PLAUSIWERT guid 11149
>     #PRINT Die vom System gelieferte GUID lautet $guid$.
>     *3:SS/S:ABGANG/GUID:$guid$

> Wird die Plausi nicht gefunden oder kann der Wert nicht extrahiert werden, dann wird die Variable `<name>` gelöscht.

`#PRINT_VARS`

> Gibt alle momentan verwendeten Variablen alphabetisch sortiert im Logfile aus. Neben den Namen werden die Typen und deren Inhalte ausgegeben.

`#ARRAY_CREATE``<name> [colname[:coltype] ...]`

> Legt ein neues zwei-dimensionales Array in der Variable `<name>` an. In dieses können zeilen- und spaltenbasierte Daten eingestellt werden, die aus CSV-Dateien (mit `#ARRAY_LOAD`) oder aus der letzten HIT-Abfrage (mit `#ARRAY_ADD_HIT`) stammen. Nach dem Anlegen mit `#ARRAY_CREATE` ist das Array leer.

> Beim Anlegen eines Arrays können optional Spaltennamen und -typen mitgegeben werden (durch Leerzeichen getrennt):

>     #ARRAY_CREATE demo NAME:String STR_NR PLZ:Integer ORT:java.lang.String

> Die Typen entsprechen exakt denen von Java. Normalerweise müssen sie in der sogenannten Fully Qualified Name (FQN)-Schreibweise angegeben werden, aber die Standard-Typen aus dem Namespace `java.lang.*` und `java.math.*` dürfen abgekürzt angegeben werden (im Beispiel bei `NAME`, verglichen mit der vollen Schreibweise bei `ORT`). Wird durch `:` getrennt kein Typ angegeben, wird automatisch `String` angenommen (im Beispiel bei `STR_NR`).

> Details zur Verwendung des `<name>` im Javatest finden sich hier.

`#ARRAY_ADD``<name> <spalte> [spalte...]`

> Erzeuge eine neue Datenzeile und hänge sie an das Array `<name>` an und fülle sie mit 1..n `<spalte>`n.

> Eine `<spalte>` darf wiederum ein CSV-String sein, der vor dem Hinzufügen in die Datenzeile am CSV-Trennzeichen `;` aufgesplittet wird. Jeder dieser Befehle

>     #ARRAY_ADD demo  eins;zwei;drei;vier
>     #ARRAY_ADD demo  eins zwei drei vier
>     #ARRAY_ADD demo  eins zwei;drei;vier
>     #ARRAY_ADD demo  eins zwei;drei vier
>     #ARRAY_ADD demo  eins;zwei drei;vier

> fügt somit die gleichen vier Datenspalten der Datenzeile hinzu.

> Ist `<name>` vorher noch nicht verwendet worden, legt `#ARRAY_ADD` automatisch ein neues Array an. Da so keine Typen und Spaltennamen übergeben werden können, enthält das neue Array keine Typen und dynamisch generierte Spaltennamen (aufsteigende Zahlenfolge).

> Umgekehrt bedeutet dies bei einem bestehenden Array mit Spaltennamen und/oder Typen, dass die hier angegebenen Daten (vorerst ungeprüft(!)) übernommen werden.

`#ARRAY_ADD_HIT``<name> [append]`

> Lese alle Spalten und Zeilen aus der letzten HIT-Antwort in das mit `<name>` angegebene Array.

> Wird als zweiter Parameter `append` oder `1` angegeben, werden die Daten an bereits bestehende Daten im Array angehängt - vorausgesetzt, die Spaltenzahl und ggf. die Spaltentypen passen zusammen. Ohne den zweiten Parameter werden bestehende Daten gelöscht, bevor neue Daten gespeichert werden (Warnung: widerspricht etwas dem Befehlsnamen, ist aber Absicht so!).

> Ist `<name>` vorher noch nicht verwendet worden, legt `#ARRAY_ADD_HIT` automatisch ein neues Array an.

> Wird die Abfrage (mit `RS`), die `#ARRAY_ADD_HIT` in ein Array einlesen soll, mit dem Subcode `V1` abgesetzt, merkt sich der Befehl die Spaltentypen, indem es diese in Java-Pendants übersetzt und speichert. Ohne den Subcode wird jede Spalte als `java.lang.String` aufgefasst.

`#PRINT_ARRAY``<name>`

> Gib das Array `<name>` mit seinen Datentypen im Logfile aus.

`#ARRAY_ROWS_BW``<name> <min> <max>`

> Prüft, ob das Array `<name>` mindestens `<min>` und maximal `<max>` Datenzeilen enthält.

`#ARRAY_ROWS_EQ``<name> <num>`

> Prüft, ob das Array `<name>` genau `<num>` Datenzeilen enthält.

`#ARRAY_SAVE``<name> <dateiname> [append]`

> Speichere alle Spalten und Zeilen des mit `<name>` angegebenen Arrays in die angegebene Datei `<dateiname>` (ggf. in Anführungszeichen `""`). Es wird das CSV-Format verwendet, d.h. es wird ggf. an den Dateinamen noch `.csv` angehängt. Existiert die Variable `<name>` nicht, dann bricht der Javatest ab.

> Wird als zweiter Parameter `append` oder `1` angegeben, werden die Daten an bereits bestehende Daten in der Datei angehängt. Ohne den zweiten Parameter wird die Zieldatei neu angelegt, bevor neue Daten gespeichert werden.

> #### Spaltentypen

> Als besonderes Feature speichert der Befehl in einer zweiten Datei mit dem Namen `<dateiname>.types.csv` die Typen der einzelnen Spalten. Diese Typendatei besteht nur aus zwei Zeilen: Kopf- und Typenzeile. Diese Datei wird beim Einlesen mit `#ARRAY_LOAD` mitberücksichtigt, sollte sie existieren. Damit kann ein Typabgleich zwischen zu lesenden und bereits vorhandenen Daten stattfinden. Ein Spaltentyp hat die Form `package.Classname` (gemäß der Java-Spezifikation).

> Die Typen können entweder manuell in einer `.types.csv`-Datei angegeben und dann mit `#ARRAY_LOAD` mitberücksichtigt werden
> oder
> via `#ARRAY_ADD_HIT` automatisch mit eingelesen werden, wenn beim `RS` der Subcode `V1` angegeben wurde.

> Beispiel:

>     *3:RS/V1:CODES/CODESET;CODENR;CODE;CODETEXT:CODESET;EQ;RASSE
>     #ARRAY_ADD_HIT ergebnis
>     #ARRAY_SAVE ergebnis ausgabe.csv

> `ergebnis` enthält nun alle `RASSE`-Schlüssel und auch die Typen der vier Spalten. Mit `#ARRAY_SAVE` werden alle vier Spalten jeder Rasse inklusive Kopfzeile in die Datei `ausgabe.csv` geschrieben:

>     CODESET;CODENR;CODE;CODETEXT
>     RASSE;1;SBT;Holstein-Sbt
>     RASSE;2;RBT;Holstein-Rbt
>     ...
>     RASSE;97;XFF;Kreuzung Fleischrind x Fleischrind
>     RASSE;98;XFM;Kreuzung Fleischrind x Milchrind
>     RASSE;99;XMM;Kreuzung Milchrind x Milchrind

> Zusätzlich wird eine Typen-Datei `ausgabe.types.csv` angelegt, die die gleiche Kopfzeile und eine Zeile mit den Typen enthält:

>     CODESET;CODENR;CODE;CODETEXT
>     java.lang.String;java.lang.Long;java.lang.String;java.lang.String

`#ARRAY_LOAD``<name> <dateiname> [append]`

> Lese alle Spalten und Zeilen aus der angegebenen Datei `<dateiname>` (ggf. in Anführungszeichen `""`) in das mit `<name>` angegebene Array. Eine zusätzlich vorhandene Typen-Datei wird mitberücksichtigt, wobei die Anzahl Spalten und Spaltennahmen exakt übereinstimmen müssen. Mehr dazu und zur Dateinamensemantik siehe `#ARRAY_SAVE`.

> Wird als zweiter Parameter `append` oder `1` angegeben, werden die Daten an bereits bestehende Daten im Array angehängt - vorausgesetzt, die Spaltenzahl und ggf. die Spaltentypen passen zusammen. Ohne den zweiten Parameter werden bestehende Daten gelöscht, bevor neue Daten gespeichert werden.

> Ist `<name>` vorher noch nicht verwendet worden, legt `#ARRAY_LOAD` automatisch ein neues Array an.

`#SUBST_CREATE_TEXT``<name> <seed> <min> <max> [words] [charset [charsetLC]]`

> Definiert einen Substitutor mit Namen `<name>`, der beliebige Texte durch dynamisch generierte Texte ersetzt.

> `<seed>` legt einen numerischen Startwert für den Zufallsgenerator fest. Ist er `0`, wird ein nicht reproduzierbarer Zahlenstrom generiert (sollte immer so angegeben werden). Alle anderen Werte erlauben reproduzierbare Texte.

> Mit `<min>` und `<max>` werden die Zeichenkettenlängen festgelegt, die der neu generierte String haben soll. Die Länge wird zufällig ermittelt und liegt zwischen beiden Werten (jeweils einschließlich). `<min>` und `<max>` dürfen identisch sein, so dass man damit immer Zeichenketten der gleichen Länge erzeugen kann.

> Schließlich legt `[charset]` fest, welche Zeichen der neue String haben darf. Jedes Zeichen, einschließlich aller Weißraumvarianten, ist dabei zulässig. Ohne die Angabe eines `[charset]` wird der minimalistische Zeichenvorrat `"xX"` verwendet.

> Beispiel:

>     #SUBST_CREATE_TEXT demo 0 10 16 1 aBcDeFg

> Der Substitutor erzeugt Zeichenketten der zufälligen Länge zwischen 10 und 16 Zeichen, der aus zufällig zusammengesetzten Zeichen des Zeichenvorrats `aBcDeFg` besteht (ohne Leerzeichen, da nur `1` Wort). Etwa: `FgBeccFeacD`.

> Statt einem konkreten Zeichenvorrat können auch folgende Konstanten angegeben werden, welche dann intern durch bestimmte konkrete Zeichenvorräte ersetzt werden:

> Konstante | Zeichenvorrat
> --- | ---
> `default` | `xX`
> `lower` | `abcdefghijklmnopqrstuvwxyz`
> `upper` | `ABCDEFGHIJKLMNOPQRSTUVWXYZ`
> `alnum` | `ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789`
> `de_upper` | `ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ`
> `de_lower` | `abcdefghijklmnopqrstuvwxyzäöüß`
> `de_euro` | `ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ abcdefghijklmnopqrstuvwxyzäöüß 0123456789 €`
> `de_white` | `ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ abcdefghijklmnopqrstuvwxyzäöüß 0123456789 €` mit Leerzeichen und Tabulator
> `ascii` | `!"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` abcdefghijklmnopqrstuvwxyz{|}~`
> `latin1 iso-8869-1` | `!"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ`
> `latin9 iso-8859-15` | `!"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£€¥Š§š©ª«¬®¯°±²³Žµ¶·ž¹º»ŒœŸ¿ ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ`

> Wenn man wider Erwarten doch einen Zeichenvorrat haben möchte, der tatsächlich aus den Zeichen z.B. `ascii` bestehen soll, dann verschiebt man einfach ein Zeichen willkürlich, etwa zu `iasci`.

> Werden zwei `[charset]` angegeben, dann wird der erste Zeichenvorrat für den ersten Buchstaben eines Wortes und der zweite Vorrat für die restlichen Buchstaben eines Wortes verwendet. So kann man beispielsweise recht einfach Wörter erzeugen, die jeweils mit einem Grossbuchstaben beginnen und Kleinbuchstaben enden:

>     #SUBST_CREATE_TEXT demo 0 70 70 5 upper lower

> Da die Generierung vollständig zufällig geschieht, sind Rückschlüsse auf das Original weder über die Länge der Zeichenkette, noch über die Position von Weißraum (sofern im Zeichenvorrat angegeben bzw. wenn Anzahl Wörter > 1), noch über die einzelnen Zeichen möglich.

`#SUBST_CREATE_BNR``<name> <start-bnr> ["alnum"]`

> Definiert einen Substitutor mit Namen `<name>`, der konkrete Betriebsnummern durch dynamisch generierte andere ersetzt. Identische Betriebsnummern erhalten dementsprechend identische Ersetzungen.

> Für jede zu ersetzende Betriebsnummer wird intern, ausgehend von der Start-BNR `<start-bnr>`, aufsteigend eine neue erzeugt und vermerkt. Es findet keinerlei Prüfung auf korrekte BNR statt, d.h. der Substitutor kann auch leicht für andere Zwecke genutzt werden.

> Als optionalen dritten Parameter kann das Wort `alnum` (genau so) angegeben werden. Damit werden die Betriebsnummern intern gleich als alpha-numerische BNR weiterverarbeitet und beim Rückwärts-Ersetzen auch genau so geliefert.

> Beispiel:

>     #SUBST_CREATE_BNR demo 276097878787878

> Dieser Substitutor erzeugt folgende BNR: `276097878787878`, `276097878787879`, `276097878787880`, `276097878787881`, `276097878787882`, `276097878787883`, `276097878787884` und so weiter.

`#SUBST_CREATE_LOM``<name> <startwert> ["alnum"]`

> Definiert einen Substitutor mit Namen `<name>`, der konkrete Ohrmarkennummern (kurz LOMs) durch dynamisch generierte andere ersetzt. Identische LOMs erhalten dementsprechend identische Ersetzungen.

> Da man bei LOMs unterschiedliche europäische Länder zur Verfügung stehen, wird die zu ersetzende LOM in ILAND (3-stellig) und den Rest (12-stellig) zerlegt. Nun wird für jedes erkannte ILAND eine eigene Ersetzungstabelle angelegt, d.h. alle `DE`-LOMs werden aufsteigend ersetzt, alle österreichischen (`AT`) LOMs ebenso und so weiter. Eine ungültige LOM (z.B. Dummy-LOMs) wird automatisch zu einer `DE`-LOM (Anm.: wird wohl nicht so bleiben, wird evtl. mal geändert).

> Der Startwert für jedes ILAND wird mit `<startwert>` angegeben und darf maximal 12-stellig sein.

> Als optionalen dritten Parameter kann das Wort `alnum` (genau so) angegeben werden. Damit werden die LOMs intern gleich als alpha-numerische LOM weiterverarbeitet und beim Rückwärts-Ersetzen auch genau so geliefert. Achtung! Durch das banale Hochzählen neuer LOM-Nummern können bei einigen EU-Ländern sehr leicht ungültige LOMs erzeugt werden, die dann durch `alnum` in der Form `#NNNNNNNNNNNNNNN` zwischengespeichert werden und zurückübersetzt werden!

> Beispiel:

>     #SUBST_CREATE_LOM demo 12121212

> Dieser Substitutor erzeugt diese LOM-Liste

>     DE 01 000 00012, AT 023 464 706, DE 01 000 00036, DE 01 000 00082,
>     CZ 310105 219, 980011080000009

> folgende Ersetzungen

>     276000012121212, 40000023464706, 276000012121213, 276000012121214,
>     203000012121213, 276000012121215

> Wie unschwer zu erkennen, ist die `DE`-LOM nun ungültig, da sie kein BLAND mehr enthält. Es ist daher zu empfehlen, eine 10-stellige Nummer mit gültigem BLAND (`01`-`16`) zu verwenden, beispielsweise `0912121212`.

`#SUBST_CREATE_EMAIL``<name> <seed> <max> <domain-suffix>`

> Definiert einen Substitutor mit Namen `<name>`, der Email-Adressen mit einem gegebenen `<domain-suffix>` erzeugt. Die maximale Länge ist hierbei `<max>`. Intern wird die Länge der Emailadresse anhand `<max>` aufgeteilt: eine Hälfte ist für den lokalen Teil vor dem `@`, die andere für den Domain-Teil nach dem `@`.

> `<seed>` legt einen numerischen Startwert für den Zufallsgenerator fest. Ist er `0`, wird ein nicht reproduzierbarer Zahlenstrom generiert (sollte immer so angegeben werden). Alle anderen Werte erlauben reproduzierbare Texte.

>     #SETVAR demo Irgendwas
>     #SUBST_CREATE_MAIL sEmail 0 33 .example.com
>     #SUBST_REPLACE sEmail demo

> liefert z.B. in demo den Wert `eGah4FxPL@2cZZ.example.com`.

`#SUBST_CREATE_TEXTUUID``<name> [useIC]`

> Definiert einen Substitutor mit Namen `<name>`, der Text durch zufällig erzeugte UUIDs ersetzt.

> Wird `useIC` als optionaler Parameter angegeben, dann wird beim Text die Groß-/Kleinschreibung ignoriert.

>     #SETVAR demo Irgendwas
>     #SUBST_CREATE_TEXTUUID sUuid useIC
>     #SUBST_REPLACE sUuid demo
>     #SETVAR demo IrGeNdWaS
>     #SUBST_REPLACE sUuid demo

> liefert z.B. in demo sowohl für `Irgendwas` als auch für `IrGeNdWaS` den Wert `59d10446-5b83-4d3c-904f-1eea57f5e85d`.

`#SUBST_REPLACE``<name> <variable> [params ...]`

> Ersetze anhand des Substitutors `<name>` den Inhalt der Variable `<variable>` durch einen dynamisch generierten Wert. `<variable>` muss vorher gesetzt worden sein. Beispiel:

>     #SETVAR text Lorem ipsum
>     #SUBST_CREATE_TEXT demo 0 0 16
>     #SUBST_REPLACE demo text

> Die Variable `text` wird hier anhand des Substitutors `demo` durch einen zufällig generierten Text ersetzt.

> Damit konkrete Werte beispielsweise einer Zeile ersetzt werden können, sind weitere Parameter nötig:

>     ; Abfrage in Array $betriebe$:
>     *2:RS/S:BTR_D/BNR15;NAME;NAME2;STR_NR;PLZ;ORT;ORTSTEIL: _
>             BNR15;BW;09 199 000 0000;09 199 000 9999;ORDER;1
>     ; Fülle Array mit den Betriebsdaten
>     #ARRAY_ADD_HIT betriebe
>     ; Substitutor für Name in $fremde$
>     #SUBST_CREATE_TEXT fremde 4711 10 30 2 latin1
>     ; nun zeilenweise alle Betriebe durchgehen
>     #FORALL_ARRAY betriebe row
>        ; ersetze Spalte NAME in der Row anhand Referenz:
>        ;    unter Verwendung des Substitutors "fremde" via
>        ;    Zeile "row" die Spalte "NAME" ersetzen
>        #SUBST_REPLACE fremde row NAME
>        ; zeige ganze Zeile als CSV an - die Spalte NAME ist
>        ;    nun ersetzt durch etwas zufälliges
>        #PRINT ersetzt: $row$
>     #NEXT
>     #PRINT Beispiel-NAME der 3ten Zeile: $betriebe[3,NAME]$.

> Über zusätzliche (durch Leerzeichen getrennte) Parameter lassen sich auch Listen- und Array-Elemente ersetzen, indem Index-Variablen oder -Werte übergeben werden. Die Parameter müssen als Zugriffskette logisch aufbauen, d.h. erst ggf. Array, dann Liste, dann Einzelelement. Die Parameterliste im Beispiel `row NAME` sagt aus, dass ausgehend von der aktuellen Zeile `row` die konkrete Spalte `NAME` verarbeitet werden soll. Würde man diese Zugriffskette nicht angeben, würden lediglich lokale Variablen ersetzt, die nach dem Verlassen der Schleife obsolet würden.

> Ein dem Substitutor nicht bekannter Wert wird dabei durch die Substitutor-Vorschrift neu generiert, intern vermerkt und dann in `<variable>` gesetzt. Bei bekannten Werten wird dessen Pendant aus der internen Liste ermittelt und in `<variable>` gesetzt. Die Substitutor-Vorschrift hängt vom verwendeten Substitutor ab, d.h. der BNR-Substitutor generiert fortlaufende Betriebsnummern oder der Text-Substitutor zufällig erzeugte Zeichenketten.

> Eine Ersetzung kann auch direkt als Variablenausdruck `$<name>[<variable>]$` vorgenommen werden, ohne dass `#SUBST_REPLACE` als Zwischenschritt verwendet werden muss. Hat auch den Vorteil, dass der Wert der Variablen `<variable>` unverändert bleibt.

`#SUBST_REVERSE``<name> <variable> [params ...]`

> Ersetze anhand des Substitutors `<name>` den Inhalt der Variable `<variable>` durch dessen Original. `<variable>` muss vorher gesetzt worden sein.

> Damit die Rück-Übersetzung funktioniert, muss sich das Original in der internen Liste befinden. Existiert es nicht, bricht das Script ab.

> Eine direkte Rück-Ersetzung als Variablenausdruck analog der normalen Ersetzung existiert nicht.

`#SUBST_SAVE``<name> <dateiname>`

> Speichert den Substitutor `<name>` mit dessen Übersetzungstabelle in die angegebene Datei `<dateiname>` (ggf. in Anführungszeichen `""`). Es wird das CSV-Format verwendet, d.h. es wird ggf. an den Dateinamen noch `.csv` angehängt. Existiert die Variable `<name>` nicht, dann bricht der Javatest ab.

> Als besonderes Feature speichert der Befehl in einer zweiten Datei mit dem Namen `<dateiname>.options.csv` die Parameter und insbesondere den Typ des Substitutors.

> Beispiel anhand eines BNR-Substitutors:

>     #SUBST_CREATE_BNR uebersetzung 276097878787878
>     *2:RS:BTR_D/BNR15:BNR15;BW;09 199 000 0000;09 199 000 9999;ORDER;1
>     #FORALL_HIT row
>        #SETVAR tmp $row[BNR15]$
>        #SUBST_REPLACE uebersetzung tmp
>        #PRINT vorher: $row[BNR15]$ -> nachher: $tmp$
>     #NEXT
>     #SUBST_SAVE uebersetzung tabelle.csv

> Der Substitutor mit Namen `uebersetzung` wird mit dem Startwert `276097878787878` inituialisiert und wird innerhalb der `#FORALL_HIT`-Schleife via Variable tmp gefüllt. tmp wird dabei jeweils auf einen Ersetzungswert gesetzt.

> Beim Speichern in die Datei `tabelle.csv` wird etwa folgende Tabelle abgelegt:

>     ORIGINAL;SUBSTITUTION
>     276091990005312;276097878787930
>     276091990000023;276097878787900
>     276091990000040;276097878787917
>     ...
>     276091990000015;276097878787892
>     276091990000038;276097878787915

> Die Liste ist nach der ersten Spalte sortiert.

> Damit beim Einladen der richtige Substitutor mit dessen Parametern wieder identisch reproduziert werden kann, speichert #ARRAY_SAVE noch folgendes in die Datei `tabelle.options.csv`:

>     Substitutor;NextBNR;StartBNR;Steps
>     Javatest.hitscript.subst.BnrSubst;276097878787941;276097878787878;1

> Die Spalte `Substitutor` enthält den Typen des Substitutors und die restlichen Spalten dessen Parameter, die je nach verwendetem Substitutor variieren.

`#SUBST_LOAD``<name> <dateiname>`

> Lese die Übersetzungstabelle und dessen Parameter aus der angegebenen Datei `<dateiname>` (ggf. in Anführungszeichen `""`) und legen einen neuen Substitutor in der Variable `<name>` ab. Eine zusätzlich vorhandene Parameter-Datei wird mitberücksichtigt. Mehr dazu und zur Dateinamensemantik siehe `#SUBST_SAVE`.

> Ist `<name>` vorher noch nicht verwendet worden, legt `#SUBST_LOAD` automatisch einen neuen Substitutor an.

`#PRINT_SUBST``` `<name>`

> Gib die Übersetzungstabelle des Substitutors `<name>` im Logfile aus.

`#SET_BLOBMARKER``` `<def>`

> Definiert einen Blob-Marker, mit dem Ausdrücke wie `@action(filepath)` ersetzt werden (wenn der Blob-Marker `@` ist).

`#SET_BLOB_TEXTUUID``` `<name>`

> Merkt sich den Variablennamen des Substitutors für die automatische Ersetzung von sogenannten Blobs, die mit `#SET_BLOBMARKER` beginnen.

> Wichtig: Der Substitutor muss bereits vorher durch einen der #SUBST_CREATE-Befehle oder einem `#SUBST_LOAD` angelegt worden sein und am Ende des HitScripts ggf. per `#SUBST_SAVE` gesichert werden. Existiert der Variablenname mit passendem Typ (ein Substitutor vom Typ TextUUID) nicht, dann bricht der Javatest ab.

`#IFVAR``<name> <op> <wert>`

> Prüft anhand eines Operators `<op>`, ob der Inhalt der Variable `<name>` und der gewünschte Wert `<wert>` passen.

> Ist eine Bedingung erfüllt, dann wird der nachfolgende Block analog `#IFDEF` ausgeführt. Wenn nicht, dann erst wieder nach dem `#ELSE` oder `#ENDIF`.

> Mögliche Operatoren `<op>` sind:

> | Der nachfolgende Block wird ausgeführt, wenn ...
> `EQ`, `=` | ... die Variable `<name>` den Wert `<wert>` besitzt.
> `NE`, `!=`, `<>` | .. die Variable `<name>` nicht den Wert `<wert>` besitzt.
> `LT`, `<` | .. die Variable `<name>` "kleiner" dem Wert `<wert>` ist.
> `LE`, `<=` | .. die Variable `<name>` "kleiner gleich" dem Wert `<wert>` ist.
> `G` `T`, `>` | .. die Variable `<name>` "größer" dem Wert `<wert>` ist.
> `GE`, `>=` | .. die Variable `<name>` "größer gleich" dem Wert `<wert>` ist.
> `IN` | .. der Wert `<wert>` als Teilzeichenkette in der Variable `<name>` vorkommt.
> `NI` | .. der Wert `<wert>` als Teilzeichenkette nicht in der Variable `<name>` vorkommt.

> Derzeit werden nur Zeichenkettenvergleiche durchgeführt.

> Existiert die Variable nicht oder ist der Operator unbekannt, dann bricht der Javatest ab.

`#IF``<wert1> <op> <wert2>`
`#ELSEIF``<wert1> <op> <wert2>`

> Prüft anhand eines Operators `<op>`, ob der linke Wert `<wert1>` und der rechte Wert `<wert2>` passen.

> Ist eine Bedingung erfüllt, dann wird der nachfolgende Block analog `#IFDEF` ausgeführt. Wenn nicht, dann erst wieder nach dem `#ELSEIF`, `#ELSE` oder `#ENDIF`.

> Als Operatoren gelten die gleichen wie bei `#IFVAR`, nur eben `<wert1>` statt Inhalt der Variable `<name>`.

> Generell werden Zeichenkettenvergleiche (case-sensitive) durchgeführt. Lassen sich beide Vergleichswerte jedoch in Zahlen umwandeln, wird numerisch verglichen.

> Existiert die Variable nicht oder ist der Operator unbekannt, dann bricht der Javatest ab.

`#FORALL``<name> <index>
` `#FORALL_VAR``<name> <index>`

> Enthält eine Variable `<name>` einen CSV-String (d.h. Werte, die durch Semikolon `;` getrennt sind), dann kann man mit der `#FORALL`-Schleife über alle Werte zyklisch die Indexvariable `<index>` füllen lassen.

> Innerhalb einer Schleife sind Sprünge mit `#GOTOLINE` und `#SKIP` nicht erlaubt und führen zu einem Fehler.

> Beispiel:

> `#SETVAR autos Audi;BMW;Opel;Alfa
> #FORALL autos i
> #PRINT $i$ ergänzen
> #UPDATEVAR alle $alle$ +
> #SETVAR alle $alle$$i$
> #ENDFORALL
> #PRINT komplett: $alle$`

> liefert im Logfile

> `Audi ergänzen
> BMW ergänzen
> Opel ergänzen
> Alfa ergänzen
> komplett: Audi + BMW + Opel + Alfa`

> In erster Linie ist die `#FORALL`-Schleife für's Abarbeiten von Listen gedacht, weniger für zyklische Zeichenkettenverarbeitung, wie im Beispiel gezeigt.

> Eine Schleife wird mit `#NEXT` abgeschlossen.

`#FOR <name> <start> <end> [step] [digits]`
`#FORALL_STEPS <name> <start> <end> [step]``[digits]`

> Ein `#FOR` bildet einen einfachen Zähler ab. Beginnend bei einem Startwert `<start>` wird mit einer Schrittweite `[step]` (Vorgabe: `1`) solange hochgezählt, bis der Endwert `<end>` erreicht ist. Der Wert bei jedem Durchlauf wird in der angegebenen Variable `<name>` gespeichert. `[step]` darf auch negativ sein, jedoch nicht `0`.

> Wird `[digits]` nicht angegeben, wird die Variable `<name>` mit dem numerischen Wert des Zählers belegt.
> Ist `[digits]` jedoch angegeben (numerische Werte zwischen 0 und 99), dann wird der numerische Wert als Zeichenkette interpretiert und vorne mit so vielen `0`en aufgefüllt, bis die Länge `[digits]` erreicht ist. Ist der Zähler als Zeichenkette "länger" als `[digits]`, dann wird der Wert nicht gestutzt (z.B. ein `[digits]` von `2` gibt trotzdem `1000` aus)! Ist beispielsweise hilfreich, wenn ein Ohrmarkenbereich über eine Schleife abgedeckt werden soll, der immer fünf Ziffern benötigt (aus einem Schleifenwert z.B. `47` wird dann `00047`). Damit `[digits]` angegeben werden kann, muss ein ggf. fehlendes `[step]` mit `1` angegeben werden.

> Innerhalb einer Schleife sind Sprünge mit `#GOTOLINE` und `#SKIP` nicht erlaubt und führen zu einem Fehler.

> Beispiel:

> `#FOR i 1 100 3
> #PRINT $i$
> #NEXT`

> Die Schleife gibt alle Werte zwischen 1 und 100 in 3er-Schritten aus: 1, 4, 7, ..., 94, 97, 100.

> `[digits]`-Beispiel:

> `#FOR i 1 100 1 5
> #PRINT DE 01 234 $i$
> #NEXT`

> Die `5` (4. Parameter) zeigt an, dass der Indexwert in der Variable `i` immer fünfstellig sein soll. Damit wird jede einzelne Ohrmarkennummer `DE 01 234 00001` bis `DE 01 234 00100` ausgegeben.

`#FORALL_HIT <name>`

> Wurde vor dieser Schleife eine HITP-Abfrage (mit Command `RS` o.ä.) durchgeführt, dann kann mit `#FORALL_HIT` jede Ergebniszeile abgearbeitet werden. Der `<name>` wird dabei als Array-Prefix verwendet, d.h. er wird bei jedem Durchlauf mit den Werten der einzelnen Spalten als Array-Index in der Form `<name>[spaltennummer]` und `<name>[spaltenname]` gefüllt.

> Innerhalb einer Schleife sind Sprünge mit `#GOTOLINE` und `#SKIP` nicht erlaubt und führen zu einem Fehler.

> Beispiel:

> `*3:RS:CODES/CODESET;CODENR;CODE;CODETEXT:CODESET;EQ;RASSE
> #FORALL_HIT col
> #PRINT Code $col[2]$: $col[CODETEXT]$
> #NEXT`

> Die Schleife gibt folgendes aus:

> `Code 1: Holstein-Sbt
> Code 2: Holstein-Rbt
> Code 3: Jersey
> ...
> Code 97: Kreuzung Fleischrind x Fleischrind
> Code 98: Kreuzung Fleischrind x Milchrind
> Code 99: Kreuzung Milchrind x Milchrind`

> Die Variable `$col[2]$` wird hierbei durch den Wert der zweiten Spalte ersetzt, die Variable `$col[CODETEXT]$` durch den Wert mit dem Spaltennamen `CODETEXT`. Auch hier gilt wie bei allen Variablen die korrekte Groß-/Kleinschreibung!

`#FORALL_FILE <name> <dateiname> [header_ignorieren]`

> `#FORALL_FILE` öffnet anhand `<dateiname>` eine CSV-Datei und verarbeitet jede gelesene Zeile als CSV. Der `<name>` wird dabei als Array-Prefix verwendet, d.h. er wird bei jedem Durchlauf mit den Werten der einzelnen Spalten als Array-Index in der Form `<name>[spaltennummer]` gefüllt. Der optionale Parameter `[header_ignorieren]` ermöglicht es, eine mögliche Kopfzeile zu überspringen (= `1`, Vorgabe) oder sie auch als Daten aufzufassen (= `0`).

> Innerhalb einer Schleife sind Sprünge mit `#GOTOLINE` und `#SKIP` nicht erlaubt und führen zu einem Fehler.

> Ein

> `#FORALL_FILE col C:/data/adressen.csv
> #PRINT Mitgliedsnummer $col[1]$: $col[2]$ aus $col[4]$ $col[5]$
> #NEXT`

> liefert beispielsweise Zeilen wie

> `Mitgliedsnummer 12: Heinz Müller aus 12345 Musterhausen`

> NB: Der `<dateiname>` darf leider keine Leerzeichen enthalten!

`#FORALL_SELECT <name> <sql_select>`

> Per DB-Zugriff (sofern aktiviert in Ini-Datei) kann der gegebene SELECT `<sql_select>` ausgeführt werden. Ein fehlendes Schlüsselwort `SELECT` zu Beginn von `<sql_select>` wird automatisch hinzugefügt; zudem wird ein überflüssiges Semikolon `;` am Ende automatisch entfernt. Der `<name>` wird dabei als Array-Prefix verwendet, d.h. er wird bei jedem Durchlauf mit den Werten der einzelnen Spalten als Array-Index in der Form `<name>[spaltennummer]` und `<name>[spaltenname]` gefüllt.

> Innerhalb einer Schleife sind Sprünge mit `#GOTOLINE` und `#SKIP` nicht erlaubt und führen zu einem Fehler.

> Analog dem Beispiel oben bei `#FORALL_HIT`:

> `#FORALL_SELECT col select * from %SqlQualifier%CODES where codeset = 'RASSE';
> #PRINT Code $col[2]$: $col[4]$
> #NEXT`

> liefert das selbe Ergebnis wie bei `#FORALL_HIT`.

> Da ein `SELECT` die einzelnen Spalten mit ihren Datentypen liefert, kann man innerhalb einer solchen Schleife (also nur in `#FORALL_SELECT`) eine Ausgabeformatierung verwenden. Das Format ist `<name>[spaltennummer]:FORMAT` bzw. `<name>[spaltenname]``:FORMAT` . Als `FORMAT` kann verwendet werden:

> Formatierbefehl | wird formatiert als
> --- | ---
> `:DATE` | TT.MM.JJJJ
> `:TIME` | hh.mm.ss
> `:TS` | TT.MM.JJJJ/hh.mm.ss.f
> `:SQLDATE` | 'TT.MM.JJJJ'
> `:SQLTIME` | 'hh.mm.ss'
> `:SQLTS` | 'JJJJ-MM-TT-hh.mm.ss.ffffff'
> `:SQLSTR` | 'string'

> Damit lassen sich leicht neue SQL-Statements bauen. Selbst NULL-Werte werden korrekt als `NULL` abgebildet. Inkompatible Datentypen (z.B. String als Timestamp ausgeben) werden nicht ersetzt, d.h. die Variablenangabe bleibt unverändert stehen und es wird auch kein Fehler generiert.

> Ein

> `$col[DBET_BIS]:SQLTS$`

> gibt beispielsweise `'2100-12-31-00.00.00.000000'` aus. Ohne den Formatierbefehl so: `2100-12-31 00:00:00.0`.

`#FORALL_DIRS <name> <dirspec> [dirspec ...]`

> Erlaubt es, ein oder mehrere Verzeichnisse mit einem Schleifendurchlauf zu durchsuchen und anhand von Mustern gewünschte Dateinamen zu filtern. Bei jedem Durchlauf werden Informationen über die nächste gefundene Datei der Variablen mit dem Array-Prefix `<name>` zugewiesen.

> Das Format einer `<dirspec>` ist `<dir> [options] [pattern ...]` mit

> - `<dir>`: absolutes und existierendes Verzeichnis als Basisverzeichnis
> - `[options]`: derzeit gibt es folgende Optionen
>   - `/R` aktiviert das rekursive Durchsuchen des Basisverzeichnisses
>   - `/I <name>` ignoriert das Verzeichnis mit dem Namen <name> beim rekursiven Durchsuchen (ist ohne `/R` wirkungslos)
> - `[pattern]`: ein oder mehrere Dateimuster mit den Platzhaltern `*` und `?`, die auf Dateien im Basisverzeichnis (oder Unterverzeichnissen) passen sollen. Zusätzlich kann dem Muster ein `^` vorangestellt werden, um die Logik umzukehren (d.h. eine Datei, die nicht auf das Muster paßt)

> Es können mehrere `<dirspec>`s angegeben werden, die der Reihe nach und getrennt voneinander abgearbeitet werden. Am Ende der Abarbeitung ergibt dies eine Liste aller gefundener Dateien, die dann in der Schleife abgearbeitet werden. Es ist je nach `<dirspecs>`-Konstellation möglich, dass Dateien mehrfach auftreten können, da die Ergebnisliste nicht auf doppelte Einträge überprüft wird.

> Es gibt einen Sonderfall: wird statt `<dir>` eine konkrete Datei angegeben, dann wird diese als separate `<dirspec>` der Form `<Verzeichnis der konkreten Datei> <Dateiname ohne Platzhalter>` interpretiert.

> Verzeichnis-, Datei- und Musterangaben können in doppelte Hochkomma (`"`) eingeschlossen werden, wenn in diesen Leerzeichen vorkommen. Sonst werden die einzelnen Elemente des Befehls am Leerzeichen getrennt, was z.B. bei `C:\Program Files\HitBatch` zu Problemen führen wird.

> Informationen jeder gefundenen Datei werden dann Variablen mit dem Prefix `<name>` zugewiesen:

> Datei-Information | Variable
> --- | ---
> Voller absoluter Pfad der Datei | `<name>[fullpath]`
> Basis-Verzeichnis (wie er bei `<dirspecs>`, s.o., angegeben wurde) | `<name>[basedir]`
> Verweis auf die Datei, relativ zum Basis-Verzeichnis ggf. auch mit Unterverzeichnissen, wenn rekursiv abgearbeitet | `<name>[relfile]`
> dito, formatiert zur Verwendung in URLs | `<name>[urlrelfile]`
> Verweis auf das Verzeichnis der Datei, relativ zum Basis-Verzeichnis ggf. auch mit Unterverzeichnissen, wenn rekursiv abgearbeitet | `<name>[reldir]`
> Name der Datei (ohne Verzeichnisangaben) | `<name>[name]`
> dito, formatiert zur Verwendung in URLs | `<name>[urlname]`
> Dateierweiterung ohne führenden Punkt (z.B. `mp3` bei `musik.mp3`) | `<name>[ext]`
> Größe der Datei in Bytes (unformatiert) | `<name>[size]`
> Lfd. Nummer der Datei in der `#FORALL_DIRS`-Liste | `<name>[num]`

> Innerhalb einer Schleife sind Sprünge mit `#GOTOLINE` und `#SKIP` nicht erlaubt und führen zu einem Fehler.

> Beispiel:

> `#FORALL_DIRS file D:\HIT *.java ^D?A*.java
> #PRINT Mein Name ist $file[name]$ und bin $file[size]$ Bytes groß.
> #``NEXT`

> Es werden alle Dateien im Verzeichnis `D:\HIT` (nur dort, nicht in Unterverzeichnissen) gesucht, die dem Muster `*.java` entsprechen. Da ein negatives Muster angegeben wurde (`^D?A*.java`), werden diese aus der Ergebnisliste gelöscht. Danach wird diese Liste Datei für Datei abgearbeitet und jeweils `file[]` gefüllt.

`#FORALL_ARRAY <array> <rowname>`

> Arbeitet jede Datenzeile des Arrays `<array>` ab. Der `<rowname>` wird dabei als Array-Prefix verwendet, d.h. er wird bei jedem Durchlauf mit den Werten der einzelnen Spalten als Array-Index in der Form `<name>[spaltennummer]` und `<name>[spaltenname]` gefüllt.

> Neben den Spaltenzugriffen existieren folgende Sonderindexe:

> > Spalten- Index | wird ersetzt durch | Beispiel | liefert z.B.
> > --- | --- | --- | ---
> > ohne | `null``` | `$row$` | `276090000000001;Merkel;Berlin`
> > `` `#``` | Anzahl Spalten in der Zeile | `$row[#]$` | `3`
> > `spalte` | Spaltenname der gegebenen Spalte | `$row[3]$` | `ORT`

> Innerhalb einer Schleife sind Sprünge mit `#GOTOLINE` und `#SKIP` nicht erlaubt und führen zu einem Fehler.

> Beispiel:

>     #ARRAY_LOAD rassen rassen.csv

>     #FORALL_ARRAY rassen row
>        #PRINT Code $row[2]$: $row[CODETEXT]$
>        #PRINT $row[#]$ Spalten mit $row$
>     #NEXT

`#FORALL_LIST <row> <colval>`

> Arbeitet die gegebene Datenzeile `<row>` ab und stellt den Wert der aktuellen Spalte in der Variablen `<col` `val``>` bereit.

`#NEXT`
`#ENDFORALL`

> Eine Schleife (egal welche) läuft durch bis zum `#ENDFORALL` bzw. `#NEXT` und wiederholt sich solange, bis alle Werte abgearbeitet sind.

> Innerhalb einer Schleife verwendete Variablen werden nach dem Ende der Schleife vollständig zurückgesetzt auf die Werte, die sie vor dem Start der Schleife hatten!

`#START_LOM <lom>
` `#NEXT_LOM`

> Definiert man mit `#START_LOM` eine numerische LOM `<lom>` (15stellig), dann kann man sich das eigenhändige Hochzählen von neuen LOM-Nummern sparen, indem man in Befehlen die Variable `$LOM$` einfügt. Das Hochzählen übernimmt dann der Befehl `#NEXT_LOM`.

`#SET_LOM_RANGE``<untergrenze> <obergrenze>`

> Um das gelegentliche fehlerträchtige Ändern von Ohrmarkenbereichen für SQL-Statements einfacher zu gestalten, kann man hier mit `<untergrenze>` und `<obergrenze>` einen Bereich festlegen. Der Wert steht dann in der Variable `$SqlLomRange$` in Form von `between <untergrenze> and <obergrenze>`, die dann in Statements angegeben werden kann, z.B.:

> `#SET_LOM_RANGE 276000914500000 276000914599999
> #EXEC_SQL delete from %SqlQualifier%ERSTERF where lom $SqlLomRange$;`

> Dies würde dann z.B. in folgendes übersetzt:

> `delete from HTX.ERSTERF where lom between 276000914500000 and 276000914599999;`

`#SET_BNR_RANGE``<untergrenze> <obergrenze>`

> Um das gelegentliche fehlerträchtige Ändern von Betriebsnummernbereichen für SQL-Statements einfacher zu gestalten, kann man hier mit `<untergrenze>` und `<obergrenze>` einen Bereich festlegen. Der Wert steht dann in der Variable `$SqlBnrRange$` in Form von `between <untergrenze> and <obergrenze>`, die dann in Statements angegeben werden kann, z.B.:

> `#EXEC_SQL delete from %SqlQualifier%BTR_D where bnr15 $SqlBnrRange$;`

`#HITP_QUOTE``<name> [param ...]
` `#HITP_UNQUOTE``<name> [param ...]`

> Maskiert (=`#HITP_QUOTE`) oder demaskiert (=`#HITP_UNQUOTE`) den Wert in der gegebenen Variable `<name>`.

> Über zusätzliche (durch Leerzeichen getrennte) Parameter lassen sich auch Listen- und Array-Elemente maskieren, indem Index-Variablen oder -Werte übergeben werden. Die Parameter müssen logisch aufbauen, d.h. erst ggf. Array, dann Liste, dann Einzelelement. Beispiel:

>     ; Abfrage HIT
>     *2:RS/S:BTR_D/BNR15;NAME;NAME2;STR_NR;PLZ;ORT;ORTSTEIL:BNR15;BW;09 199 000 0000;09 199 000 9999;ORDER;1
>     ; jede Zeile durchgehen
>     #FORALL_HIT row
>       #FORALL_LIST row col
>           #HITP_QUOTE row col
>        #NEXT
>        #PRINT die Zeile maskiert: $row$
>     #NEXT

> Eine Angabe `#HITP_QUOTE col` (ohne `row`) funktioniert nicht, da sonst nur der Wert in `col` geändert würde und nicht der entsprechende Wert in `row`!

`#SETHITVAR` <name>=<wert> [name=wert ...]

> Setzt 1 .. n Schlüssel-Wert-Paare für HIT-Ausdrücke. Ein Schlüssel-Wert-Paar `<name>=<wert>` wird ohne Leerzeichen zusammengeschrieben. Enthält der Wert Leerzeichen, dann den Wert in ".." einschließen. Die " werden dabei nicht ausgegeben. Die gespeicherten Daten werden automatisch als lokale Variablen `$hitFelder$` und `$hitDaten$` gespeichert, z.B.:

> `#SETHITVAR BNR15=276090000000001 LOM="DE 09 12345678" ABGA_DAT=12.06.2006
> *2:XS:ABGANG/$hitFelder$:$hitDaten$`

> wird ersetzt durch

> `*2:XS:ABGANG/BNR15;ABGA_DAT;LOM:276090000000001;12.06.2006;DE 09 12345678`

> Wichtig dabei ist, dass durch die Art der internen Speicherung die Reihenfolge der hinzugefügten Felder "willkürlich" ist. Die Felder werden nicht in der Reihenfolge ausgegeben, wie sie hinzugefügt wurden!

> Nicht erkannte Ausdrücke werden ignoriert.

`#REMHITVAR``<name> [name ...]`

> Löscht 1 .. n Schlüssel samt deren Werte aus den Hit-Ausdrücken. Unbekannte Schlüssel werden ignoriert. Die lokalen Variablen `$hitFelder$` und `$hitDaten$` werden dann aktualisiert.

> Wird `all` als einziger Parameter verwendet, dann werden alle Hit-Ausdrücke samt Werte gelöscht.

### HIT-Protokoll

`#ALWAYS_BW``<untergrenze> <obergrenze>`

> Setzt ein globales Intervall für die Schwere aller folgenden HIT-Antworten. Antworten außerhalb der Grenzen `<untergrenze>` und `<obergrenze>` führen zum Abbruch des Scripts. Default sind `-9999` und `9999`.

`#ZEILEN_ZAHL_BW``<untergrenze> <obergrenze>`

> Prüft, ob die Anzahl der Antwortzeilen der letzten HIT-Anwort zwischen `<untergrenze>` und `<obergrenze>` liegt.

`#SCHWERE_EQ` `<schwere>`

> Prüft, ob die Schwere der letzten HIT-Anwort gleich dem Wert `<schwere>` ist.

`#SCHWERE_BW``<untergrenze> <obergrenze>`

> Prüft, ob die Schwere der letzten HIT-Anwort zwischen den Werten `<untergrenze>` und `<obergrenze>` liegt.

`#PLAUSI_COUNT` `<plausinr> <anzahl>`

> Prüft, ob die Anzahl der Plausi `<plausinr>` `<anzahl>` mal in der letzten HIT-Anfwort enthalten ist.

`#PLAUSI_EXIST_ALL``[plausinr] ...`

> Prüft, ob sämtliche angegebenen Plausis `[plausinr] ...` in der letzten HIT-Anfwort enthalten sind. Zusätzlich zu den gegebenen dürfen weitere existieren.

> Wurde mit `#ALWAYS_IGNORE_PLAUSI` eine Liste von immer zulässigen Plausis angelegt, werden diese mit einbezogen.

`#PLAUSI_EXIST_ONE``[plausinr] ...`

> Prüft, ob eine der angegebenen Plausis `[plausinr] ...` in der letzten HIT-Anfwort enthalten ist.

> Wurde mit `#ALWAYS_IGNORE_PLAUSI` eine Liste von immer zulässigen Plausis angelegt, werden diese mit einbezogen.

`#PLAUSI_NOT_EXIST``[plausinr] ...`

> Prüft, ob keine der angegebenen Plausis `[plausinr] ...` in der letzten HIT-Anfwort enthalten ist.

`#PLAUSI_EXIST_ONLY``[plausinr] ...`

> Prüft, ob genau sämtliche der angegebenen Plausis `[plausinr] ...` in der letzten HIT-Anfwort enthalten sind.

> Wurde mit `#ALWAYS_IGNORE_PLAUSI` eine Liste von immer zulässigen Plausis angelegt, werden diese mit einbezogen.

`#ALWAYS_IGNORE_PLAUSI``[plausinr] ...`

> Definiert eine Liste von Plausis `[plausinr] ...`, die bei den Befehlen `#PLAUSI_EXIST_ONLY`, `#PLAUSI_EXIST_ALL` und `#PLAUSI_EXIST_ONE` grundsätzlich als "vorhanden" betrachtet werden, d.h. diese müssen bei den Befehlen nicht explizit angegeben werden.

> Wird keine Plausinummer angegeben, wird die hinterlegte Liste geleert, d.h. es gibt fortan keine immer zulässigen Plausinummern, und die genannten Befehle verhalten sich gemäß übergebener Argumente.

`#PRINT_STATUS``[status]`

> Gibt die Plausis und Fehler der letzten HIT-Antwort aus. Ist eine Meldung `[status]` angegeben, wird dieser als Status ausgeben.

`#PRINT_DATEN``[kopfzeile]`

> Gibt die Daten der letzten HIT-Antwort in ihrer "Rohform" aus. Ist eine Meldung `[kopfzeile]` angegeben, wird dieser als Vorspann ausgeben.

`#PRINT_DATENX``[kopfzeile]`

> Gibt die Daten der letzten HIT-Antwort in aufbereiteter Form aus. Ist eine Meldung `[kopfzeile]` angegeben, wird dieser als Vorspann ausgeben.

`#WRITE_DATEN``[kopfzeile]`

> Gibt die Daten der letzten HIT-Antwort in ihrer "Rohform" in die aktuelle Ausgabedatei aus. Die Ausgabedatei muss vorher mit `#SET_OUTFILE` definiert werden. Ist eine Meldung `[kopfzeile]` angegeben, wird dieser als Vorspann ausgeben.

`#SPALTE_MIT_TEXT``<text>`

> Prüft, ob in einer Spalte der letzten HIT-Antwort-Datensätze der Text `<text>` enthalten ist. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird.

`#SPALTE_OHNE_TEXT``<text>`

> Prüft, ob in einer Spalte der letzten HIT-Antwort-Datensätze der Text `<text>` nicht enthalten ist. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird.

`#HEAD_MIT_TEXT``<text>`

> Prüft, ob in einer Überschrift der letzten HIT-Antwort-Datensätze der Text `<text>`enthalten ist.

`#HEAD_OHNE_TEXT``<text>`

> Prüft, ob in einer Überschrift der letzten HIT-Antwort-Datensätze der Text `<text>`nicht enthalten ist.

`#ZEILE_MIT_TEXT``<text>`

> Prüft, ob in einer Zeile der letzten HIT-Antwort-Datensätze der Text `<text>` enthalten ist. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird.

`#ZEILE_OHNE_TEXT``<text>`

> Prüft, ob in einer Zeile der letzten HIT-Antwort-Datensätze der Text `<text>` nicht enthalten ist. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird.

`#ZEI_N_MIT_TEXT``<text>`

> Prüft, ob in der `<zeile>`ten Zeile der letzten HIT-Antwort-Datensätze der Text `<text>` enthalten ist. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird. Für `<zeile>` sind Werte zwischen 1 bis Anzahl Zeilen möglich.

`#ZEI_N_OHNE_TEXT``<text>`

> Prüft, ob in der `<zeile>`ten Zeile der letzten HIT-Antwort-Datensätze der Text `<text>` nicht enthalten ist. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird. Für `<zeile>` sind Werte zwischen 1 bis Anzahl Zeilen möglich.

`#SPALTE_MIT_REGEX``<text>`

> Prüft, ob in einer Spalte der letzten HIT-Antwort-Datensätze der Text `<text>` enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird. Ob die gesamte Spalte oder nur ein Teil davon geprüft wird, legt `#REGEX_EXACT_MATCH` fest.

`#SPALTE_OHNE_REGEX``<text>`

> Prüft, ob in einer Spalte der letzten HIT-Antwort-Datensätze der Text `<text>` nicht enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird.

`#ZEILE_MIT_REGEX``<text>`

> Prüft, ob in einer Zeile der letzten HIT-Antwort-Datensätze der Text `<text>` enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird. Ob die gesamte Zeile oder nur ein Teil davon geprüft wird, legt `#REGEX_EXACT_MATCH` fest.

`#ZEILE_OHNE_REGEX``<text>`

> Prüft, ob in einer Zeile der letzten HIT-Antwort-Datensätze der Text `<text>` nicht enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird.

`#ZEI_N_MIT_REGEX` <zeile>:<text>

> Prüft, ob in der `<zeile>`ten Zeile der letzten HIT-Antwort-Datensätze der Text `<text>` enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird. Für `<zeile>` sind Werte zwischen 1 bis Anzahl Zeilen möglich. Ob die gesamte Zeile oder nur ein Teil davon geprüft wird, legt `#REGEX_EXACT_MATCH` fest.

`#ZEI_N_OHNE_REGEX``` `<zeile>:``<text>`

> Prüft, ob in der `<zeile>`ten Zeile der letzten HIT-Antwort-Datensätze der Text `<text>` nicht enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Zu beachten ist, dass die Datensatzposition zurückgesetzt wird. Für `<zeile>` sind Werte zwischen 1 bis Anzahl Zeilen möglich.

`#REGEX_EXACT_MATCH <bool>`

> Stellt den RegEx-Vergleichsmodus ein, der für alle folgenden regulären Ausdrücke gilt. `1` ist Default.

> `0` | reguläre Ausdrücke sollen nur auf einen Teil des Prüftextes passen
> `1` | reguläre Ausdrücke sollen auf den gesamten Prüftext passen

> Man kann mit Modus `0` auch den kompletten Prüftext prüfen, wenn man dem regulären Ausdruck vorne ein `^` und hinten ein `$` anhängt.

`#IS_VALID_LOM``<lom>`

> Prüft die `<lom>` auf ihre Korrektheit. Bricht ab, wenn sie falsch ist und gibt die Fehlermeldung dazu zurück. Bricht jedoch nicht ab, wenn `#ALWAYS_BW` so gesetzt ist, dass mindestens Schwere 2 den Test nicht abbricht.

`#IS_INVALID_LOM``<lom>`

> Prüft die `<lom>` auf ihre "Falschheit". Bricht ab, wenn sie wider erwarten korrekt ist und gibt die numerische LOM dazu zurück. Bricht jedoch nicht ab, wenn `#ALWAYS_BW` so gesetzt ist, dass mindestens Schwere 2 den Test nicht abbricht.

`#IS_VALID_SW_LOMB` <lom>

> Prüft die Schweine-`<lom>` (Betriebsohrmarken) auf ihre Korrektheit. Bricht ab, wenn sie falsch ist und gibt die Fehlermeldung dazu zurück. Bricht jedoch nicht ab, wenn `#ALWAYS_BW` so gesetzt ist, dass Schwere 3 den Test nicht abbricht.

`#IS_INVALID_SW_LOMB``<lom>`

> Prüft die Schweine-`<lom>` (Betriebsohrmarken) auf ihre "Falschheit". Bricht ab, wenn sie wider erwarten korrekt ist und gibt die LOM dazu zurück. Bricht jedoch nicht ab, wenn `#ALWAYS_BW` so gesetzt ist, dass Schwere 3 den Test nicht abbricht.

`#IS_VALID_SZ_LOMB``<lom>`

> Prüft die Schafe/Ziegen-`<lom>` (Betriebsohrmarken) auf ihre Korrektheit. Bricht ab, wenn sie falsch ist und gibt die Fehlermeldung dazu zurück. Bricht jedoch nicht ab, wenn `#ALWAYS_BW` so gesetzt ist, dass Schwere 3 den Test nicht abbricht.

`#IS_INVALID_SZ_LOMB``<lom>`

> Prüft die Schafe/Ziegen-`<lom>` (Betriebsohrmarken) auf ihre "Falschheit". Bricht ab, wenn sie wider erwarten korrekt ist und gibt die LOM dazu zurück. Bricht jedoch nicht ab, wenn `#ALWAYS_BW` so gesetzt ist, dass Schwere 3 den Test nicht abbricht.

`#IS_VALID_SZ_LOMS``<lom>`

> Prüft die Schafe/Ziegen-`<lom>` (Einzeltier-Ohrmarken) auf ihre Korrektheit. Bricht ab, wenn sie falsch ist und gibt die Fehlermeldung dazu zurück. Bricht jedoch nicht ab, wenn `#ALWAYS_BW` so gesetzt ist, dass Schwere 3 den Test nicht abbricht.

`#IS_INVALID_SZ_LOMS``<lom>`

> Prüft die Schafe/Ziegen-`<lom>` (Einzeltier-Ohrmarken) auf ihre "Falschheit". Bricht ab, wenn sie wider erwarten korrekt ist und gibt die LOM dazu zurück. Bricht jedoch nicht ab, wenn `#ALWAYS_BW` so gesetzt ist, dass Schwere 3 den Test nicht abbricht.

`#PLAUSI_MIT_TEXT``<text>`

> Prüft, ob in einer Plausi der letzten HIT-Antworten der Text `<text>` enthalten ist.

`#PLAUSI_OHNE_TEXT``<text>`

> Prüft, ob in einer Plausi der letzten HIT-Antworten der Text `<text>` nicht enthalten ist.

`#PLS_N_MIT_TEXT``<text>`

> Prüft, ob in der `<zeile>`ten Plausi der letzten HIT-Antworten der Text `<text>` enthalten ist. Für `<zeile>` sind Werte zwischen 1 bis Anzahl Plausis möglich.

`#ZEI``_N``_OHNE_TEXT``<text>`

> Prüft, ob in der `<zeile>`ten Plausi der letzten HIT-Antworten der Text `<text>` nicht enthalten ist. Für `<zeile>` sind Werte zwischen 1 bis Anzahl Plausis möglich.

`#PLAUSI_MIT_REGEX``<text>`

> Prüft, ob in einer Plausi der letzten HIT-Antworten der Text `<text>` enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Ob die gesamte Plausi oder nur ein Teil davon geprüft wird, legt `#REGEX_EXACT_MATCH` fest.

`#PLAUSI_OHNE_REGEX``<text>`

> Prüft, ob in einer Plausi der letzten HIT-Antworten der Text `<text>` nicht enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck.

`#PLS_N_MIT_REGEX` <zeile>:<text>

> Prüft, ob in der `<zeile>`ten Plausi der letzten HIT-Antworten der Text `<text>` enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Für `<zeile>` sind Werte zwischen 1 bis Anzahl Plausis möglich. Ob die gesamte Plausi oder nur ein Teil davon geprüft wird, legt `#REGEX_EXACT_MATCH` fest.

`#PLS_N_OHNE_REGEX``` `<zeile>:``<text>`

> Prüft, ob in der `<zeile>`ten Plausi der letzten HIT-Antworten der Text `<text>` nicht enthalten ist. Der Text `<text>` ist ein Perl-kompatibler regulärer Ausdruck. Für `<zeile>` sind Werte zwischen 1 bis Anzahl Plausin möglich.

`#RECONNECT``[connect timeout] [server timeout]`

> baut die Verbindung zum Server wieder auf, wenn z.B. Antwort der Schwere 4 (Panik) die Verbindung unterbrochen hat.
> Optionale Parameter sind der Connect- und Connection-Timeout (beide angeben).

`#SET_HITP_SERVER``<host> <port>`

> Beendet in jedem Fall die aktuelle Verbindung zu einem HitServer und baut eine neue zum Host `<host>` an Port `<port>` auf.

> Wird gar kein Parameter angegeben, dann übernimmt Javatest wieder die Standardeinstellungen aus der Ini-Datei.

`#CSV_UPLOAD``<command> <entity> <filename> [<csv-in> <header-template> <input-template>]`

> Der Inhalt der CSV-Datei wird mit dem angegebenem Befehl `<command>` als Meldung mit dem Namen `<entity>` gesendet. Arbeitet ähnlich wie Batch-Client oder Massen-Upload im Onlineprogramm. Pfadangaben in Dateinamen spezifiziert man am besten mit Slash (c:/tmp/xx.csv) statt mit Backslash.

> `<csv-in>` legt fest wie das CSV-Format interpretiert wird. `0` (strict) ist Default.

> `0` | striktes CSV, d.h. hex-encoded, `%--` als NULL ... (default)
> `1` | readable, d.h. mit Strings in Hochkomma, Hochkomma verdoppelt

> Analog können mit optionalen Templates die Überschriftenzeilen mittels `<header-template>` und Datenzeilen mittels `<input-template>` modifiziert werden (Details siehe.Doku Batch-Client)

> Bricht ab, wenn eine Fehlerschwere außerhalb der mit `#ALWAYS_BW` gesetzten Fehlergrenzen auftritt.

> Beispiele:
> `#CSV_UPLOAD IS ZUGANG JavaTest/cvs_upload_zugang.csv
> #CSV_UPLOAD * ZUGANG JavaTest/cvs_upload_zugangX.csv
> #CSV_UPLOAD IS ZUGANG JavaTest/cvs_upload_zugang.csv #[2];#[1];#[3];MELD_DAT #[2];#[1];#[3];#[3]`

`#HITBATCH``<inifile>`

> Mit der gegebenen Ini-Datei `<inifile>` wird ein HitBatch gestartet.

> Bricht ab, wenn eine Fehlerschwere außerhalb der mit `#ALWAYS_BW` gesetzten Fehlergrenzen auftritt.

### SQL

Alle in diesem Abschnitt befindlichen Befehle sind nur für die ZDB von Bedeutung, da diese eine direkte Datenbankverbindung voraussetzen, die von extern nicht aufgebaut werden kann.

`#EXEC_SQL`

> Führt ein SQL-Statement aus (sofern aktiviert in Ini-Datei). `INSERT`; `DELETE` und `UPDATE` sind möglich.

`#SET_AUTOCOMMIT``[<Ja>]`

> Ändert den in der Konfiguration (INI-Datei) festgelegten Wert für AUTOCOMMIT - zum Setzen des Auto-Commit Verhaltens der `#EXEC_SQL`-Befehle.

> `[Ja]` setzt das Verhalten

> `1` | AutoCommit AN
> `0` | AutoCommit AUS
> (ohne) | ohne Angabe erfolgt Zurücksetzen auf Wert in Konfiguration

`#INSERT_PIN_SYS``<bnr>;<mbn>;<pin>[;<SYS_VON>;<SYS_BIS>]`
`#INSERT_PIN_SELF``<bnr>;<mbn>;<pin>[;<SYS_VON>;<SYS_BIS>]`

> Da PINs nicht unverschlüsselt direkt eingefügt werden können, hilft dieser Befehl aus: er erzeugt die verschlüsselte PIN und speichert diese für den Benutzer `<bnr>` (und `<mbn>`) in der entsprechenden Tabelle. Zusätzlich kann optional der Gültigkeitszeitraum mit `<SYS_VON>;<SYS_BIS>` angegeben werden. Sämtlliche Parameter ausser `<pin>` müssen Datenbank-tauglich sein!

> Bei `#INSERT_PIN_SYS` wird bei `MELD_BNR` der Systembetrieb 0 eingefügt, wohingegen bei `#INSERT_PIN_SELF` bei `MELD_BNR` der Wert von `<bnr>` und `MELD_MBN` der Wert von `<mbn>` verwendet wird.

> Die Parameterliste verhält sich hier leider etwas anders: nur das Semikolon `;` ist hier der Parameter-Trenner, nicht das Leerzeichen (die Parameter dürfen nämlich Leerzeichen enthalten)!

`#CHECK_PIN_INTEGRITY``<bnr>;<mbn>`

> Überprüft den PIN-Eintrag für den Benutzer `<bnr>` (und `<mbn>`), ob er in sich stimmig ist.

> Bricht ab, wenn dies nicht gegeben ist, unabhängig von der Fehlerschwere.

### Aposti

`#TEST_LOM``[lom]`

> Nimmt die angegebene numerische Ohrmarke `[lom]` und berechnet für das Tier sämtliche Aposti-Vorgänge (sofern aktiviert in Ini-Datei). Wird `[lom]` nicht angegeben, dann wird die automatische LOM verwendet, die mit `#START_LOM` gesetzt und mit `#NEXT_LOM` hochgezählt wird. Wurde weder `[lom]` angegeben noch eine automatische LOM definiert, beendet sich der Befehl mit einem Fehler.

> Ist `[lom]` nicht numerisch, wird versucht, mit der üblichen LOM-Umschlüsselungslogik eine numerische Form zu ermitteln. Ist dies nicht möglich, weil die LOM keine gütige EU-LOM ist, wird mit einem Fehler abgebrochen.

> Es werden alle errechneten Plausis -getrennt nach VVVO und Veterinäre- und der Lebenslauf des Tieres gespeichert. In der Datenbank werden physikalisch Daten geändert, gelöscht und gespeichert! Damit kann man sich den Testfall sofort im Web ansehen.

> Nach erfolgreichem Berechnen wird die Anzahl der Vorgänge ausgegeben.

`#PRINT_VVVO_VORGAENGE`

> Gebe die Plausinummern der mit `#TEST_LOM` berechneten VVVO-Vorgänge aus.

`#PRINT_VET_VORGAENGE`

> Gebe die Plausinummern der mit `#TEST_LOM` berechneten Veterinär-Vorgänge aus.

`#PRINT_LEBENSLAUF``[lom]`

> Gibt den Lebenslauf des Tieres mit der gegebenen numerischen Ohrmarke `[lom]` aus (wie er in Aposti verwendet wird). Wird `[lom]` nicht angegeben, dann wird die automatische LOM verwendet, die mit `#START_LOM` gesetzt und mit `#NEXT_LOM` hochgezählt wird. Wurde weder `[lom]` angegeben noch eine automatische LOM definiert, beendet sich der Befehl mit einem Fehler.

`#HAS_VVVO_VORGANG``[plausinr] ...`

> Vergleicht die zuletzt mit `#TEST_LOM` berechneten offenen VVVO-Vorgänge mit sämtlichen angegebenen `[plausinr] ...`. Bei einer Abweichung wird mit Fehler abgebrochen.

`#HAS_VVVO_ZOMBIES``[plausinr] ...`

> Vergleicht die zuletzt mit `#TEST_LOM` berechneten VVVO-Vorgänge, die als weggedrückt gekennzeichnet wurden, mit sämtlichen angegebenen `[plausinr] ...`. Bei einer Abweichung wird mit Fehler abgebrochen.

`#HAS_VET_VORGANG``[plausinr] ...`

> Vergleicht die zuletzt mit `#TEST_LOM` berechneten Veterinär-Vorgänge mit sämtlichen angegebenen `[plausinr] ...`. Bei einer Abweichung wird mit Fehler abgebrochen.

`#APOSTI_PLAUSIS_IN_DB``<lom> [plausinr] ...`

> Ruft physikalisch via SQL (sofern aktiviert in Ini-Datei) alle Vorgänge zur gegebenen LOM `<lom>` ab und vergleicht deren Plausinummern.mit sämtlichen angegebenen `[plausinr] ...`. Bei einer Abweichung wird mit Fehler abgebrochen.

> Im Gegensatz zu den anderen Aposti-Testfunktionen verarbeitet der Befehl die VVVO- und Veterinärvorgänge gemeinsam, d.h. es müssen Plausinummern beider "Kategorien" angeben werden!

### Webseiten

`#HTTP_GET``<url>`

> Holt eine URL `<url>` via HTTP GET Request. Eventuelle Parameter müssen zuvor via z.B. `#FORM_SET` oder `#FORM_ADD` vorbereitet werden. Eventuell vorhandene Cookies werden bei der Anfrage mitgesendet und `Set-Cookie:` Header werden ausgewertet. Beim Start des Testscripts sind keine Cookies vorhanden.

> Anmerkung: Nach dem Ausführen wird der interne Formular-Container geleert, siehe `#FORM_NEW`.

> Bricht ab, wenn eine Fehlerschwere außerhalb der mit `#ALWAYS_BW` gesetzten Fehlergrenzen auftritt.

`#HTTP_POST``<url>`

> Analog `#HTTP_GET`, jedoch wird ein HTTP POST Request abgesetzt.

`#FORM_NEW`

> Damit Formularparameter an `#HTTP_GET` und `#HTTP_POST` mitgegeben werden können, wird intern ein Formular-Container mitgeführt. Dieser wird mit diesem Befehl angelegt oder geleert.

> Mit den Befehlen `#FORM_ADD`, `#FORM_SET`, `#FORM_SET_FILE` und `#FORM_FROM_HTML` kann der Formular-Container gefüllt werden. Mit `#PRINT_FORM_DATA` wird der aktuelle Inhalt des Containers ins Logfile geschrieben.

> Achtung: Nach jedem `#HTTP_GET` bzw. `#HTTP_POST` wird der Container automatisch geleert!

`#FORM_ADD``<name>=<value>`

> Fügt einen Formulareintrag, bestehend aus dem Feldnamen `<name>` und dessen Wert `<value>`, im Formular-Container hinzu. Existiert der Feldname `<name>` bereits, dann wird `<value>` als weiterer Wert hinzugefügt. So können Mehrfachauswahlen bei Checkboxen oder Listen nachgebildet werden.

`#FORM_SET <name>=<value>`

> Setzt einen Formulareintrag, bestehend aus dem Feldnamen `<name>` und dessen Wert `<value>`, im Formular-Container. Überschreibt dabei ggf. einen bereits vorhandenen Eintrag für `<name>`.

> Hinweis: ASP.NET-Seiten verwenden das Zeichen `$` als Verschachtelungstrennzeichen in Feldnamen. Dies kann mit den Javatest-Variablen zu Konflikten führen, wenn Variablennamen gleich lauten, da diese erst ersetzt werden, bevor der Befehl ausgeführt wird. Beispiel:

>     #SETVAR login 900001
>     #FORM_SET ctl00$content$login$txtBnr15=09 000 000 0001

> Da `$login$` gleich `900001` ist, wird die Formularvariable in `ctl00$content900001txtBnr15` ersetzt, was dann für die Webseite unbrauchbar ist! Entweder lediglich die Groß-/Kleinschreibung des Variablennamen ändern, oder andere Variablennamen verwenden oder das `$` vor dem `login` mit einem `\` maskieren.

`#FORM_SET_FILE <name>=<filename>`

> Setzt einen Formulareintrag, bestehend aus dem Feldnamen `<name>` und dessen Wert `<``filename``>`, im Formular-Container. Überschreibt dabei ggf. einen bereits vorhandenen Eintrag für `<name>`. `<filename>` darf nicht in Hochkommas stehen!

> Die Besonderheit ist, dass der Wert ein Dateiname ist, dessen Datei als Upload zum Server geschickt wird.

> Wird nach dem Setzen einer Datei die Anfrage via `#HTTP_GET` durchgeführt, bricht das Script sofort ab, da Anfragen mit Dateien nur per `#HTTP_POST` möglich sind.

`#FORM_FROM_HTML [name] ...`

> Mit deser Funktion können Formularinhalte aus der letzten Abfrage (via `#HTTP_GET` oder `#HTTP_POST`) in den Formular-Container übernommen werden, um sie für die nächste Abfrage wieder zu verwenden.

> Die Funktion berücksichtigt auch vorselektierte Werte in Listen, Radiobuttons und Checkboxen. Inhalte von Knöpfen werden jedoch ignoriert, da man vor dem Ausführen einer Anfrage explizit den gedrückten Knopf angeben soll.

> Ohne Parameter übernimmt die Funktion alle Formularinhalte. Gibt man jedoch Feldnamen `[name]` an, dann werden nur diese übernommen. Als Feature kann das Wildcard-Zeichen `*` verwendet werden:

> `name` | Feldname muss exakt `name` lauten
> `name*` | Feldname beginnt mit `name`
> `*name` | Feldname endet mit `name`
> `*name`* | Feldname enthällt `name`

> Insbesondere bei der Prüfung von ZID-Webseiten ist wegen der Besonerheit von ASP.NET zwingend ein

>     #FORM_FROM_HTML __*

> vor der nächsten Abfrage einer ZID-Webseite erforderlich!

`#PRINT_FORM_DATA`

> Gibt den aktuellen Inhalt des internen Formular-Containers im Logfile aus.

`#PRINT_COOKIES`

> Gibt die alle aktuellen Cookies in lesbarer Form aus. Für Debuggingzwecke geeignet.

`#FLUSH_COOKIES`

> Der Cookie-Cache wird mit diesem Befehl gelöscht.

`#USE_PROXY``<bool>`

> Um interne und externe Abfragen absetzen zu können, lässt sich mit dem Schalter `<bool>` den Proxy dazuschalten oder nicht. Bei `0` wird kein Proxy verwendet, sonst die in der Ini-Datei angegebene Proxy-URL verwendet.

`#PRINT_HTTP_STATS`

> Gibt eine kurze Übersicht über die letzte Web-Anfrage aus: sie enthält Angaben über die URL, die Request Method, Anzahl durchgeführter Redirects, ggf. die URL des letzten Redirects, statistische Angaben über die erhaltenen HTTP-Header, den Content-Type und wie lang der Content war.

`#PRINT_HTTP_HEADERS`

> Gibt die HTTP-Header der zuletzt mit `#HTTP_GET` oder `#HTTP_POST` abgefragten Seite aus. Für Debuggingzwecke geeignet.

`#PRINT_HTTP_CONTENT`

> Gibt den kompletten Inhalt der letzten Web-Anfrage aus. Für Debuggingzwecke geeignet.

`#TEST_REDIR_URL <url>`

> Wurde eine Seite nach dem Laden umgeleitet, dann kann hiermit die URL `<url>` der letzten Umleitung getestet werden.

> Die URL ist identisch mit der URL von `#GET_WEBPAGE`, wenn keine Umleitung stattgefunden hat.

`#TEST_HTTP_STATUS`

> Prüft den Status der letzten Web-Anfrage. Dabei wird der Status in eine Fehlerschwere umgedeutet und bricht das Script ab, wenn sie außerhalb der zulässigen Grenzen von `#ALWAYS_BW` liegen.

> Umsetzungstabelle der HTTP-Statuscodes:

> `100-199` | Status "Informational" | Schwere 1
> `200-299` | Status "Success" | Schwere 0
> `300-399` | Status "Redirection" | Schwere 1
> `400-499` | Status "Client Error" | Schwere 3
> `500-599` | Status "Server Error" | Schwere 3

`#HAS_REDIRS [0/1]`

> Prüft, ob eine Weiterleitung durchgeführt wurde. 0 steht für keine Weiterleitung, 1 für mindestens eine. Stimmt die Aussage nicht, wird das Script mit Schwere 3 abgebrochen, wenn sie außerhalb der zulässigen Grenze von `#ALWAYS_BW` liegt.

`#HTTP_FIND_START`

> Setzt die Suche im Webinhalt zurück. Alle folgenden Suchbefehle beginnen dann wieder beim ersten Byte.

> Die Suchfunktionen können unabhängig von den Daten durchgeführt werden, d.h. es muss nicht zwingend ein HTML-Dokument dahinter stehen. So lassen sich auch CSV-Dateien auf korrekte Daten untersuchen.

`#HTTP_FIND_TEXT <n> <text>`

> Sucht im Webinhalt nach dem `<n>`ten Auftreten des Textes `<text>` ab der letzten Suchposition. Die Suchposition bleibt dann hinter der zuletzt gefundenen Position stehen, um mit weiteren Aufrufen mit der Suche dort fortsetzen zu können.

> Ist `<n>` keine positive Zahl, dann wird der komplette Parameter als Suchtext verwendet.

> `#HTTP_FIND_TEXT 2 Einzeltier`

> sucht das zweite Auftreten des Textes `Einzeltier`.

> `#HTTP_FIND_TEXT Alle Einzeltiere`

> sucht das erste Auftreten des Textes `Alle Einzeltiere`.

> `#HTTP_FIND_TEXT Einzeltier`

> sucht das erste Auftreten des Textes `Einzeltier`.

> `#HTTP_FIND_TEXT -4 Grad`

> sucht das erste Auftreten des Textes `-4 Grad`, da `<n>` einen ungültigen Wert enthält.

> `#HTTP_FIND_TEXT 1 3 Häuser`

> Möchte man `3 Häuser` suchen, muss man erst mal die Anzahl voranstellen, also `1`.
> `#HTTP_FIND_TEXT 3 Häuser` wäre falsch, da er sonst das dritte `Häuser` suchen würde.

`#HTTP_FIND_OHNE_TEXT <text>`

> Findet die Suche ab der letzten Suchposition den Text `<text>` im Webinhalt, dann wird ein Fehler ausgelöst.

### Zeitmessung

`#STOPWATCH_START`

> Startet die Zeitmessung.

`#STOPWATCH_GET``<variable> [fmt]`

> Ermittel aktuelle Dauer aus Zeitmessung in Millisekunden oder lesbar.

> Wird der optionale Parameter `fmt` (wie geschrieben) angegeben, wird die Zeitspanne in lesbarer Form formatiert in der angegebenen Variablen `<variable>` gespeichert.

`#STOPWATCH_RESET``<variable> [fmt]`

> Ermittel aktuelle Dauer aus Zeitmessung und setzt Zeitmessung zurück.

> Optionaler Parameter `fmt` analog oben.

`#STOPWATCH_STOP``<variable> [fmt]`

> Stoppt die Zeitmessung und speichert die gemessene Zeit in Millisekunden in die angegebene Variable `<variable>`.

> Optionaler Parameter `fmt` analog oben.

`#STOPWATCH_ASSERT``<untergrenze> <obergrenze>`

> Prüft, ob die Dauer der gestoppen Zeitmessung (in Millisekunden) zwischen den Werten `<untergrenze>` und `<obergrenze>` liegt.

`#GET_TIMESTAMP``<variable>`

> Speichert die aktuelle UNIX-Uhrzeit in Millisekunden in die angegebene Variable `<variable>`.

### Dateizugriff

`#SET_OUTFILE``<dateiname> [<append>]`

> Setzt den Namen für eine Ausgabedatei, in die mit `#WRITE_OUTFILE` geschrieben werden kann. Pfadangaben spezifiziert man am besten mit Slash (c:/tmp/xx.csv) statt mit Backslash.

> `[append]` legt fest an Datei angehängt oder ob überschrieben wird. `1` (append) ist Default.

> `1` | append, also immer anhängen, kein overwrite
> `0` | overwrite, die Datei wird geleert, kein append

> Beim Festlegen der Ausgabedatei ohne append (=`0`) wird die angegebene Datei sofort gelöscht (bzw. sie hat dann die Länge 0)!

`#WRITE_OUTFILE``[text]`

> Schreibt den Text `[text]` in die Ausgabedatei. Wurde die Ausgabedatei nicht mit `#SET_OUTFILE` definiert, beendet sich das Script mit Fehler. Die Ausgabedatei wird für diesen einen Schreibvorgang mit "append" geöffnet und hinterher gleich geschlossen. Damit können mehrere Prozesse gleichzeitig in die Datei schreiben, ohne dass sie sich (kaum) behindern.

> Das Neuanlegen einer Datei bzw. Anhängen an eine Datei wird durch `#SET_OUTFILE` gesteuert.

> Daten der zuletzt erhaltenen HIT-Datenabfrage können mit `#WRITE_DATEN` geschrieben werden.

`#FILE_COMP``<dateiname1> <``dateiname2>`

> Vergleicht die Inhalte der Dateien `<dateiname1>` und `<``dateiname2>`. Das Script bricht mit einem Fehler ab, sobald sich eine Zeile unterscheidet.

`#FILE_PRINT``<dateiname>`

> Gibt den Inhalt der gegebenen Datei `<dateiname>` im Logfile aus.

> Unabhängig von der Einstellung bei `#LINE_NUMBERS` werden dabei keine Zeilennummern ausgegeben.

### Sonstiges

`#SET_LOGFILEMAX``[<Länge>]`

> Ändert den in der Konfiguration (INI-Datei) festgelegten Wert für LOGFILEMAX - zur Begrenzung der Länge der einzelnen Ausgaben ins Logfile. Insbesondere wichtig bei binärem Datentransfer.

> `[Länge]` legt die Länge fest

> `1...n` | Ausgabe im Logfile wird begrenzt auf n Zeichen
> `0` | Ausgabe im Logfile unbegrenzt
> (ohne) | ohne Angabe erfolgt Zurücksetzen auf Wert in Konfiguration

`#SET_END_SEMIKOLON``[<Ja>]`

> Ändert den in der Konfiguration (INI-Datei) festgelegten Wert für END_SEMIKOLON - zur Steuerung ob bei der Ausgabe hinten ein Semikolon angehängt wird.

> `[Ja]` legt das Verhalten fest

> `1` | Ja, mit zusätzlichem Semikolon am Ende der Ausgabezeile
> `0` | Nein, ohne
> (ohne) | ohne Angabe erfolgt Zurücksetzen auf Wert in Konfiguration

# Reguläre Ausdrücke

Die Befehle `#SPALTE_MIT_REGEX`, `#SPALTE_OHNE_REGEX`, `#ZEILE_MIT_REGEX`, `#ZEILE_OHNE_REGEX`, `#ZEI_N_MIT_REGEX`, `#ZEI_N_OHNE_REGEX`, `#PLAUSI_MIT_REGEX`, `#PLAUSI_OHNE_REGEX` verwenden reguläre Ausdrücke, die Perl v5 kompatibel sind.

Es lassen sich auch Parameter (in runde Klammern eingeschlossene Ausdrücke) erfassen. Die Variable `$MATCH_COUNT$` liefert die Anzahl der gefundenen Parameter, einschließlich des Gesamtausdrucks. `$MATCH_0$` repräsentiert den gesamten ge"match"ten Ausdruck, `$MATCH_1$` und weitere die Parameter 1 bis `$MATCH_COUNT$`-1. Wird kein Parameter erfragt, erhält man nur den gesamten Ausdruck in `$MATCH_0$` zurück und `$MATCH_COUNT$` ist dann 1.

Nach jedem Aufruf einer der oben genannten Befehle werden diese Parameter neu gesetzt.

Beispiel:

Der Text `Der Landwirt 09 123 456 7890 hat ein Kalb mit der Ohrmarke DE 09 123 45678 lila angemalt.` soll durch den regulären Ausdruck `Landwirt ([\s\d]+).+Ohrmarke ([a-z]+[\s\d]+)` erkannt werden.
Javatest liefert dann folgende Variablen zurück:`
$MATCH_COUNT$` = `3
$MATCH_0$` = `Landwirt 09 123 456 7890 hat ein Kalb mit der Ohrmarke DE 09 123 45678`
`$MATCH_1$` = `09 123 456 7890`
`$MATCH_2$` = `DE 09 123 45678`

Kurze Syntaxübersicht:

- Beginn des Textes festlegen mit `^` (muss am Anfang stehen). `^Bau` bedeutet, dass alle Texte mit Bau beginnen müssen. Ein `Haus^bau` hat keine Wirkung.
- Ende des Textes festlegen mit `$` (muss am Ende stehen)
- der Punkt `.` definiert irgend ein Zeichen (außer Zeilenumbrüche)
- Zeichenklassen sind eine Sammlung von Zeichen, die in `[]` eingeschlossen werden. Zeichenbereiche wie `a-z` sind auch zulässig. Das Zeichen `-` selbst muss als erstes in der Klasse stehen, wenn es als solches aufgefaßt werden soll.
- negierte Zeichenklassen sind eine Sammlung von Zeichen, die an der betreffenden Stelle nicht vorhanden sein dürfen. Dazu wird als erstes Zeichen der Klasse das `^` eingefügt. Beispiel: alle Zeichen außer die Ziffern wird als `[^0-9]` geschrieben.
- Vordefinierte Zeichengruppen sind:
  - `\d` für alle Ziffern
  - `\w` für alle Klein- und Großbuchstaben plus Ziffern
  - `\s` für alle Leerzeichen, Tabulatoren und Zeilenumbrüche
  - und weitere
- Ganze Zeichenblöcke lassen sich mit `()` gruppieren. Nützlich für Wiederholungen und Parameter.
- Alternativen werden mit `|` beschrieben, wenn die Ausdrücke in Klammern `()` gefaßt sind. `(Haus|Wohnungs)bau` würde sowohl `Hausbau` als auch `Wohnungsbau` erkennen.
- Wiederholungen von Zeichen, Zeichenklassen und Gruppen mit:
  - `?` bedeutet kein oder einmal
  - `*` bedeutet kein, einmal oder mehrmals
  - `+` bedeutet einmal oder mehrmals
  - `{x}` bedeutet genau x-mal
  - `{x,y}` bedeutet x-mal bis y-mal (x <= y)
  - `{x,}` bedeutet mindestens x-mal; das Komma ist wichtig!
  - `{,y}` bedeutet maximal y-mal; das Komma ist wichtig!
- Ausdrücke in `()` werden als Parameter aufgefaßt. Nach dem Parsen erhält man die Parameter von links nach rechts durchnummeriert als `\1`, `\2` etc zurück. Ein `Haus(bau)` liefert in `\1` das Wort `bau`, wenn der gesamte reguläre Ausdruck paßt. `\0` liefert den gesamten passenden Ausdruck. Parameter dürfen geschachtelt werden - gezählt wird dann immer jede geöffnete Klammer `(`.
- Klammern `()`, die als Parameter zählen würden, können mit der Option `?:` deaktiviert werden, so dass sie nicht mitgezählt werden. Beispiel: `(?:Haus)(bau)` liefert nur in `\1` den Text `bau`.
- Wiederholungen innerhalb eines regulären Ausdruckes sind auch möglich: `(Haus)bau\1` würde beispielsweise beim Wort `HausbauHaus` passen, weil der erste Parameter `(Haus)` bei `\1` verglichen wird.
- Prüfungen auf Nicht-Vorhandensein lassen sich mit Klammern `(?!)` durchführen. Wichtig: Der Ausdruck in der Klammer wird nur zur Prüfung verwendet - er taucht nirgends im Ergebnis auf! Beispiel: `Haus(?!bau)` paßt auf `Haus`, aber nicht auf `Hausbau`. `Haustür` paßt auch nicht, da hinter dem `(?!)` kein `tür` folgt!

Die Übersicht ist bei weitem nicht vollständig. Eine gute Übersicht findet sich hier (neues Fenster öffnet sich) - es wird dort jedoch die Perl-Syntax beschrieben. Hier in Javatest wird nur der Teil zwischen `//` verwendet.

[Repo]Javatest\javatest.html - Stand: 01.07.2022