android_connector.ConfigWindowController.java Source code

Java tutorial

Introduction

Here is the source code for android_connector.ConfigWindowController.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package android_connector;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import javax.swing.JOptionPane;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;

/**
 * FXML Controller class
 *
 * @author Eric
 */
public class ConfigWindowController implements Initializable {

    Android_Connector andr;
    /**
     * Tabellenspalte, die alle Startnummern speichert
     */
    @FXML
    public TableColumn startnummer;
    /**
     * Tabellenspalte, die alle Namen speichert
     */
    @FXML
    public TableColumn name;
    /**
     * Tabellenspalte, die alle Kategorien enthlt. Aktuell funktionslos.
     */
    @FXML
    public TableColumn kategorie;
    /**
     * Tabelle auf dem Screen
     */
    @FXML
    public TableView<Person> tabelle;
    /**
     * Entlt alle Starter, die von der Klasse Person reprsentiert werden.
     */
    public ObservableList<Person> personData;
    /**
     * Fgt der tabelle eine neue Zeile hinzu.
     */
    @FXML
    public Button neueZeile;
    /**
     * Entfernt eine Tabellenzeile.
     */
    @FXML
    public Button zeileEntfernen;
    /**
     * Fllt die Spalte startnummern mit fortlaufenden Startnummern.
     */
    @FXML
    private Button startnummernGenerieren;
    /**
     * Zahl der Messstationen. Hier kann nur eine Zahl eingegeben werden.
     */
    @FXML
    public TextField messstationen;
    /**
     * Zahl der Messtore. Hier kann nur eine Zahl eingegeben werden.
     */
    @FXML
    public TextField messtore;
    /**
     * Port zur Anfrage an Datenbank. Standardmig 3306. Hier kann nur eine
     * Zahl eingegeben werden.
     */
    @FXML
    public TextField port;
    /**
     * Hostname der Datenbank. Eigentlich immer localhost.
     */
    @FXML
    public TextField host;
    /**
     * Benutzername der Datenbank. Auf Windows standardmig root
     */
    @FXML
    public TextField user;
    /**
     * Passwort fr Anmeldung. Auf Windows standardmig leer
     */
    @FXML
    public PasswordField pw;
    /**
     * Datenbankname. Standardmig android_connect.
     */
    @FXML
    public TextField db;
    /**
     * isSelected ist true, wenn (spter) die Netzwerkvrbindung vor Start
     * getestet werden soll, sonst false.
     */
    @FXML
    public CheckBox checkNetwork;
    /**
     * Zahl der Starter.
     */
    public int starter = 0;
    /**
     * isSelected ist true, wenn spter Protokolle gespeichert werden sollen,
     * sonst false
     */
    @FXML
    public CheckBox protokoll;
    /**
     * Information an User, hier den Speicherort einzugeben
     */
    @FXML
    private Label info_Ort;
    /**
     * Pfad des Ordners, in dem die Protokolle landen sollen
     */
    @FXML
    public TextField pfad;
    /**
     * Ruft DirectoryChooser auf, um Pfad zu gewnschtem Vezeichnis zu ermitteln
     */
    @FXML
    private Button pfadSuche;
    /**
     * Schliet das aktuelle Fenster
     */
    @FXML
    private Button schliessen;
    /**
     * True, wenn die Datanbank automatisch geleert werden soll, sonst false.
     */
    @FXML
    public CheckBox db_leeren;
    /**
     * Button, der einen DirectoryChooser anzeigt, in welchem man den
     * Speicherort von XAMPP auswhlt.
     */
    @FXML
    private Button xampp_suchen;
    /**
     * Hier wird der Pfad zu XAMPP eingegeben oder vom DirectoryChooser aus
     * eingetragen.
     */
    @FXML
    public TextField pfad_xampp;
    /**
     * Liest eine Excel-Datei ein.
     */
    @FXML
    private Button excelReader;
    /**
     * Schliet das Fenster ohne Aufruf der weiteren Fuktionalitten.
     */
    @FXML
    private Button schliessenButton;
    /**
     * Stellt bei Klick einen alten Zustand wieder her.
     */
    @FXML
    private Button restoreButton;
    /**
     * Auswahl, wie gestartet werden soll.
     */
    @FXML
    public ChoiceBox<String> startmodus;

    /**
     * Stellt einen alten Zustand wieder her.
     *
     * @param oldValues Instanz dieser Klasse, die zur Wiederherstellung
     * verwendet wird
     */
    public void reinit(ConfigWindowController oldValues) {
        //alle wichtigen Werte aus der alten Instanz nehmen und in den Entsprechungen der neuen Instanz speichern
        //bernahme der Werte ber Getter und Setter, da per Wertzuweisung nicht funktioniert
        this.checkNetwork.setSelected(oldValues.checkNetwork.isSelected());
        this.db.setText(oldValues.db.getText());
        //Datenbank muss geleert werden, da sonst alte Werte mit hereinpfuschen
        this.host.setText(oldValues.host.getText());
        this.messstationen.setText(oldValues.messstationen.getText());
        this.messtore.setText(oldValues.messtore.getText());
        this.name.setText(oldValues.name.getText());
        this.personData = oldValues.personData;
        this.pfad.setText(oldValues.pfad.getText());
        this.pfad_xampp.setText(oldValues.pfad_xampp.getText());
        this.port.setText(oldValues.port.getText());
        this.primaryStage = oldValues.primaryStage;
        this.protokoll.setSelected(oldValues.protokoll.isSelected());
        if (oldValues.protokoll.isSelected()) {
            //Einblenden der Oberflchenelemente
            info_Ort.setVisible(true);
            pfad.setVisible(true);
            pfadSuche.setVisible(true);
        }
        this.pw.setText(oldValues.pw.getText());
        this.starter = oldValues.starter;
        this.user.setText(oldValues.user.getText());
        this.andr = oldValues.andr;
        this.kategorien = oldValues.kategorien;
        //Zurcksetzen der DB
        (verbinder = new MySQLConnection(host.getText(), port.getText(), db.getText(), user.getText(), pw.getText(),
                null)).reset(false);
        //Tabelle ihre Werte zuweisen...
        tabelle.setItems(personData);
        //... und sie (hart) aktualisieren
        refresh_table(tabelle);
        alteWertebernommen = true;
        //Prfen, welcher Startmodus denn gewhlt war --> fr Entscheidung, wann vorbei
        this.startmodus.getSelectionModel().select(oldValues.startmodus.getSelectionModel().getSelectedItem());
    }

    /**
     * Speichert, ob die Initialisierung bereits durch die Wertbernehme von
     * einer alten Instanz geregelt wurde, dann true, sonst false.
     */
    private boolean alteWertebernommen = false;

    /**
     * Wird bei der Initialisierung des Fensters aufgerufen.
     *
     * @param url Ort der Controller-Datei
     * @param rb Ressourcen der Datei
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        //neue Liste von Startern anlegen, falls alte Liste noch nicht gelesen wurde
        if (!alteWertebernommen) {
            personData = FXCollections.observableArrayList(); // TODO
        }
        //Spalten der Tabelle zuweisen, welches Attribut der Speicherklasse Person sie auslesen sollen
        startnummer.setCellValueFactory(new PropertyValueFactory<>("Startnummer"));
        name.setCellValueFactory(new PropertyValueFactory<>("Name"));
        kategorie.setCellValueFactory(new PropertyValueFactory<>("Kategorie"));
        //Standardperson den Startern hinzufgen...
        //personData.add(e);
        //... und ihre Zahl erhhen
        //starter++;
        //Tabelle die Items zuweisen --> Darstellung
        tabelle.setItems(personData);
        //Quelle: http://www.naturalborncoder.com/java/javafx/2012/04/26/complete-javafx-2-editable-table-example/ wurde im folgenden bernommen
        //Ziel: Tabelle, in die man eingeben kann
        //bei Enter oder Doppelklick auf Tabellenzelle: Anlegen eines neuen Textfeldes, in das eingegeben werden kann und das ber der Zelle liegt
        Callback<TableColumn, TableCell> cellFactory = new Callback<TableColumn, TableCell>() {
            @Override
            public TableCell call(TableColumn p) {
                return new EditingCell();
            }
        };
        //diese Aktion wird allen Spalten zugewiesen
        startnummer.setCellFactory(cellFactory);
        //bei Enter in der Eingabe: bernahme des eingegebenen Wertes in die Tabellenzelle
        startnummer.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {
            @Override
            public void handle(CellEditEvent<Person, String> t) {
                ((Person) t.getTableView().getItems().get(t.getTablePosition().getRow()))
                        .setStartnummer(t.getNewValue());
            }
        });
        //bernahme von Namen...
        name.setCellFactory(cellFactory);
        name.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {
            @Override
            public void handle(CellEditEvent<Person, String> t) {
                ((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setName(t.getNewValue());
            }
        });
        //... und Kategorie
        kategorie.setCellFactory(cellFactory);
        kategorie.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {
            @Override
            public void handle(CellEditEvent<Person, String> t) {
                ((Person) t.getTableView().getItems().get(t.getTablePosition().getRow()))
                        .setKategorie(t.getNewValue());
            }
        });
        //Festlegung der Alternativen fr die ChoiceBox
        ObservableList<String> startmodi = FXCollections.observableArrayList();
        startmodi.add(alle_hintereinander);
        startmodi.add(nach_Kategorien);
        //Werte in ChoiceBox eintragen
        startmodus.setItems(startmodi);
        //Standardmig ersten Wert anzeigen
        startmodus.getSelectionModel().select(0);
    }

    /**
     * Angezeigter Text, wenn alle Starter hintereinander Starten, also Lufe
     * Kategorien bergeordnet sind.
     */
    public static final String alle_hintereinander = "Alle Starter laufen in 2 Lufen";
    /**
     * Anzuzeigender Text, wenn Kategorien Lufen bergeordnet sind, also alle
     * Starter einer Kategorie 3 Lufe machen, dann die der nchsten usw.
     */
    public static final String nach_Kategorien = "Starter laufen nach Kategorien getrennt.";

    /**
     * Aktion, die bei Klick auf den Button "neue Zeile" ausgefhrt wird: Zeile
     * der Tabelle hinzufgen
     *
     * @param event der Klick
     */
    @FXML
    private void neueZeile(ActionEvent event) {
        //neuer Starter hinzugefgt
        starter++;
        //markierte Zeile herausfinden --> danach Zeile einfgen durch Anlegen einer leeren Person
        Person selected = tabelle.getSelectionModel().getSelectedItem();
        if (selected != null) {
            //nach markierten Person Zeile einfgen
            personData.add(personData.indexOf(selected) + 1, new Person());
        } else {
            //standardmig am Ende Zeile anfgen
            personData.add(new Person());
        }
        //genderte Daten der Tabelle zuweisen
        tabelle.setItems(personData);
    }

    /**
     * Aktion, die bei Klick auf den Button "Zeile entfernen" ausgefhrt wird:
     * eine Zeile der Tabelle entfernen.
     *
     * @param event der Klick
     */
    @FXML
    private void zeile_Entfernen(ActionEvent event) {
        //Zahl der Starter senken
        starter--;
        //gewhlte Person ermitteln (entspricht gewhlter Zeile)
        Person selected = tabelle.getSelectionModel().getSelectedItem();
        if (selected != null) {
            //wenn vorhanden: diese entfernen
            personData.remove(selected);
        } else {
            //sonst: letzte Zeile entfernen
            personData.remove(personData.size() - 1);
        }
        //nderungen speichern
        tabelle.setItems(personData);
    }

    /**
     * Weist allen Startern automatisch nach Reihenfolge eine Startnummer zu.
     *
     * @param event der Klick auf den Button
     */
    @FXML
    private void startnummern_Generieren(ActionEvent event) {
        //alle Starter durchgehen, jedem als Startnummer seine Zeilennummer zuweisen
        for (int i = 0; i < personData.size(); i++) {
            personData.get(i).setStartnummer("" + (i + 1));
        }
        //nderungen speichern...
        tabelle.setItems(personData);
        //... und die Tabelle aktualisieren
        refresh_table(tabelle);
    }

    /**
     * Aktualisiert die Anzeige einer Tabelle durch aus- und wiedereinblenden
     * aller Zellen. Quelle:
     * http://stackoverflow.com/questions/11065140/javafx-2-1-tableview-refresh-items
     *
     * @param table die zu aktualisierende Tabelle
     */
    public static void refresh_table(TableView table) {
        for (int i = 0; i < table.getColumns().size(); i++) {
            ((TableColumn) (table.getColumns().get(i))).setVisible(false);
            ((TableColumn) (table.getColumns().get(i))).setVisible(true);
        }
    }

    /**
     * Liest die Werte der Tabelle aus und gibt diese als String-Array zurck.
     *
     * @return Array, das die Tabelle reprsentiert. Erster Index ist die
     * Zeilennummer. Zweiter Index ist dabei die Spaltennummer. Dabei steht 0
     * fr die Startnummer und 1 fr den Namen.
     * @param kategorie Kategorie, deren Starter ermittelt werden sollen
     */
    String[][] getWerte(String kategorie) {
        if (kategorie != null) {
            //zur Sicherheit: Werte aus der Tabelle erneut auslesen
            personData = tabelle.getItems();
            int starter_kategorie = 0;
            for (int i = 0; i < personData.size(); i++) {
                if (personData.get(i).getKategorie().equals(kategorie)) {
                    starter_kategorie++;
                }
            }
            //neues Array anlegen, das die Werte spter speichert
            String[][] werte = new String[starter_kategorie][2];
            //Tabelleninhalte durchgehen und in das Array einordnen wie oben beschrieben, solange sie zur gewnschten Kategorie gehren
            int zaehler = 0;
            for (int i = 0; i < personData.size(); i++) {
                if (personData.get(i).getKategorie().equals(kategorie)) {
                    werte[zaehler][0] = personData.get(i).getStartnummer();
                    werte[zaehler][1] = personData.get(i).getName();
                    zaehler++;
                }
            }
            //Rckgabe aller Werte
            return werte;
        }
        return getAllWerte();
    }

    /**
     * Liest die Werte der Tabelle aus und gibt diese als String-Array zurck.
     *
     * @return Array, das die Tabelle reprsentiert. Erster Index ist die
     * Zeilennummer. Zweiter Index ist dabei die Spaltennummer. Dabei steht 0
     * fr die Startnummer und 1 fr den Namen. 2 ist die Kategorie.
     */
    String[][] getAllWerte() {
        //zur Sicherheit: Werte aus der Tabelle erneut auslesen
        personData = tabelle.getItems();
        int starter_kategorie = 0;
        for (int i = 0; i < personData.size(); i++) {
            starter_kategorie++;
        }
        //neues Array anlegen, das die Werte spter speichert
        String[][] werte = new String[starter_kategorie][3];
        //Tabelleninhalte durchgehen und in das Array einordnen wie oben beschrieben, solange sie zur gewnschten Kategorie gehren
        int zaehler = 0;
        for (int i = 0; i < personData.size(); i++) {
            werte[zaehler][0] = personData.get(i).getStartnummer();
            werte[zaehler][1] = personData.get(i).getName();
            werte[zaehler][2] = personData.get(i).getKategorie();
            zaehler++;
        }
        //Rckgabe aller Werte
        return werte;
    }

    /**
     * Aktion, die bei Klick auf die Checkbox, die die Protokollspeicherung
     * abfragt, ausgefhrt wird.
     *
     * @param event der Klick
     */
    @FXML
    private void pfadeingabe(ActionEvent event) {
        //Label mit Anweisung, TextField zur Pfadeingabe und Button zur Pfadauswahl einblenden, wenn die Checkbox ausgewhlt ist, sonst ausblenden
        if (protokoll.isSelected()) {
            info_Ort.setVisible(true);
            pfad.setVisible(true);
            pfadSuche.setVisible(true);
        } else {
            info_Ort.setVisible(false);
            pfad.setVisible(false);
            pfadSuche.setVisible(false);
        }
    }

    /**
     * Fenster, in dem die GUI angezeigt wird. Wird vom Controller gesetzt.
     */
    public Stage primaryStage;

    /**
     * Aktion, die bei Klick auf Button "Durchsuchen" aufgerufen wird: ffnen
     * eines DirectoryChoosers zur Auswahl eines Ortes zur Protokollspeicherung.
     *
     * @param event der Klick
     * @author http://stackoverflow.com/questions/9375938/javafx-filechooser
     */
    @FXML
    private void durchsuchen(ActionEvent event) {
        //DirectoryChooser anlegen, der nur Ordner auswhlt
        DirectoryChooser chooser = new DirectoryChooser();
        //Titel und Startverzeichnis setzen
        chooser.setTitle("Speicherort fr die Protokolldateien: ");
        File defaultDirectory = new File(System.getProperty("user.home"));
        chooser.setInitialDirectory(defaultDirectory);
        //Abfrage durchfhren, die einen Ordner liefert. Muss an das Fenster, das ihn gestartet hat, angeheftet sein
        File selectedDirectory = chooser.showDialog(primaryStage);
        if (selectedDirectory != null) {
            //Pfad im Textfeld anzeigen, wenn Verzeichnis gewhlt
            pfad.setText(selectedDirectory.getAbsolutePath());
        }
    }

    /**
     * Klick auf Button lst die Aktion aus, die beim Schlieen des Fensters
     * abluft.
     *
     * @author Quelle:
     * http://stackoverflow.com/questions/24483686/how-to-force-javafx-application-close-request-programmatically
     * @param event Klick auf den Button
     */
    @FXML
    private void close(ActionEvent event) {
        primaryStage.fireEvent(new WindowEvent(primaryStage, WindowEvent.WINDOW_CLOSE_REQUEST));
    }

    /**
     * Temporre MySQLConnection ohne groen Sinn.
     */
    MySQLConnection verbinder;
    /**
     * Speichert den Identifizierer des aktuellen Betriebssystems.
     */
    private static final String OS = System.getProperty("os.name").toLowerCase();

    /**
     * Startet XAMPP als Kommandozeilenanwendung. Nur unter Windows mglich.
     * Wenn nicht Windows, dann Fehlermeldung.
     *
     * @author Quelle:
     * http://www.textpattern.net/wiki/?title=Using_XAMPP_(Apache-MySQL-PHP-Perl)_for_Windows
     * @param pfad Der Pfad zum XAMPP-Verzeichnis
     */
    public void xampp_start(String pfad) {
        //Aufruf zusammennsetzen: Pfad und Startdatei
        //vorher prfen, ob Windows vorliegt --> geht sonst nicht so
        if (OS.contains("win")) {
            String command = "\"" + pfad + "\\xampp_start.exe" + "\"";
            xampp = pfad;
            try {
                //Prozess anlegen, der die exe startet
                Process p = Runtime.getRuntime().exec(command);
                //InputStream zum Einlesen der Eingabe
                InputStream is = p.getInputStream();
                int c;
                while ((c = is.read()) != -1) {
                    //Kontrollausgabe der Konsolenantwort
                    System.out.print((char) c);
                }
                //Fehler fangen
            } catch (IOException ex) {
                //schne Fehlermeldung ausgeben
                verbinder = new MySQLConnection(new FXMLDocumentController());
                verbinder.showExceptionDialog(ex, "Fehler",
                        "XAMPP konnte aufgrund eines Fehlers nicht gestartet werden.",
                        "Bitte geben Sie bei einer Beschwerde an den Entwickler folgende Fehlermeldung an:", false);

            }
            //Nicht Windows --> Abbruch, da Konsolenaufruf so nur bei Windows funktioniert
        } else {
            Alert alert = new Alert(AlertType.WARNING);
            alert.setTitle("Bitte XAMPP starten!");
            alert.setHeaderText("Automatischer Start von XAMPP nicht mglich, bitte manuell starten!");
            alert.setContentText(
                    "Ein automatischer Start von XAMPP ist nur unter Windows mglich. Bitte starten Sie XAMPP manuell!");
            alert.showAndWait();
        }
    }

    /**
     * Speicherordner der XAMPP-Installation.
     */
    static String xampp = "";

    /**
     * Kopiert einen Ordner mit allen Dateien und Unterordnern in einen anderen
     * Ordner. Existierende Dateien werden berschrieben.
     *
     * @author Quelle:
     * http://bukkitfaq.de/forum/index.php?thread/977-ordner-kopieren-in-java/
     * @param from zu kopierender Ordner
     * @param to Zielordner
     * @throws IOException beim Auftreten irgend welcher Schreibfehler oder
     * Lesefehler
     */
    private void copyFilesInDirectory(File from, File to) throws IOException {
        //prfen, ob Zielverzeichnis existiert --> wenn nicht, dann anlegen
        if (!to.exists()) {
            to.mkdirs();
        }
        //alle untergeordneten Elemente des Quellordners durchgehen...
        for (File file : from.listFiles()) {
            //... handelt es sich um einen Ordner, dann rekursiver Aufruf der Prozedur, um ihn zu kopieren
            if (file.isDirectory()) {
                copyFilesInDirectory(file, new File(to.getAbsolutePath() + file.getName()));
                //sonst Datei an Ziel kopieren, automatisches berschreiben
            } else {
                File n = new File(to.getAbsolutePath() + "/" + file.getName());
                Files.copy(file.toPath(), n.toPath(), StandardCopyOption.REPLACE_EXISTING);
            }
        }
    }

    /**
     * Kopiert eine einzelne Datei in einen Ordner. berschreibt existierende
     * Dateien.
     *
     * @author Quelle:
     * http://bukkitfaq.de/forum/index.php?thread/977-ordner-kopieren-in-java/
     * @param file die zu kopierende Datei
     * @param to Zielordner
     * @throws IOException bei Schreibproblemen
     *
     */
    private void copyFileToDirectory(File file, File to) throws IOException {
        //Zielordner ggf. anlegen
        if (!to.exists()) {
            to.mkdirs();
        }
        //Kopieren der Datei
        File n = new File(to.getAbsolutePath() + "/" + file.getName());
        Files.copy(file.toPath(), n.toPath(), StandardCopyOption.REPLACE_EXISTING);
    }

    /**
     * berschreibt die PHP-Konfigurationsdatei mit den tatschlichen
     * Verbindungswerten des Servers. Kopiert auerdem alle fr den Betrieb
     * ntigen Scripte in den htdocs-Unterordner.
     *
     * @param pfad Pfad zur XAMPP-Installation bzw. Ordner public_html (Linux)
     */
    public void overridePHPConfig(String pfad) {
        //Schreiber initialisieren
        FileWriter fw = null;
        try {
            String res = ConfigWindowController.class.getProtectionDomain().getCodeSource().getLocation().toURI()
                    .getPath();
            res = res.substring(1, res.length() - 15);
            System.out.println("Pfad: " + res);
            //Scripte liegen unter src in JAR --> Kopieren dieser mit Methode.
            copyFilesInDirectory(new File(res + "lib/AndroidConnectorAppHTTPScripts"),
                    new File(pfad + "/htdocs/AndroidConnectorAppHTTPScripts"));
            /**
             * Enthlt den Text der PHP-Konfigurationsdatei unter Verwendung der
             * in diesem Fenster eingegebenen Konfigurationswerte.
             */
            String file = "<?php\n"
                    + "//Konfiguration.php (diese Datei) enthlt die Konfiguration der MySQL-Verbindung.\n"
                    + "//Wenn Sie einen der folgenden Werte verndert haben, mssen Sie diesen hier anpassen, damit die App ordnungsgem ausgefhrt werden kann!\n"
                    + "\n"
                    + "//Der Hostname. Lassen Sie ihn unverndert, die zugehrige IP-Adresse wird automatisch ermittelt!\n"
                    + "$host = \"" + host.getText() + "\";\n" + "\n"
                    + "//Der Benutzername fr die MySQL-Datenbank. Standardmig \"root\".\n" + "$user = \""
                    + user.getText() + "\";\n" + "\n"
                    + "//Das Passwort fr eben jene Datenbank. Unter Windows standardmig leer.\n"
                    + "$passwort = \"" + pw.getText() + "\";\n" + "\n"
                    + "//Der von Ihnen vergebene Name fr die Datenbank. Standardmig \"android_connect\";\n"
                    + "$datenbank = \"" + db.getText() + "\";\n" + "?>";
            //FileWriter anlegen, der die Datei dann schreibt
            fw = new FileWriter(pfad + "\\htdocs\\AndroidConnectorAppHTTPScripts\\Konfiguration.php");
            //schreiben und beenden
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(file);
            bw.close();
            //Fehler fangen und Exception ausgeben
        } catch (IOException ex) {
            verbinder = new MySQLConnection(new FXMLDocumentController());
            verbinder.showExceptionDialog(ex, "Fehler",
                    "Die PHP-Konfigurationsdatei konnte nicht berschrieben werden!",
                    "Die Konfigurationsdatei fr den internen PHP-Verbindungsaufbau unter \"" + pfad
                            + "\\htdocs\\AndroidConnectorAppHTTPScripts\\Konfiguration.php"
                            + "\" konnte nicht berschrieben werden."
                            + "Bitte ndern Sie ggf. die Konfiguration mit einem Texteditor!",
                    false);
        } catch (URISyntaxException ex) {
            Logger.getLogger(ConfigWindowController.class.getName()).log(Level.SEVERE, null, ex);
        } //sauber beenden
        catch (NullPointerException e) {
            MySQLConnection.staticExceptionDialog(e, "Kopierfehler",
                    "Bentigte PHP-Skripte konnten nicht kopiert werden!",
                    "Die fr die App-Verbindung ntigen PHP-Skripte konnten nicht automatisch "
                            + "kopiert werden. Bitte kopieren Sie sie mit Ausnahme von \"Konfiguration.php\" manuell in den Ordner \"XAMPP\\htdocs\"!");
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException ex) {
                verbinder = new MySQLConnection(new FXMLDocumentController());
                verbinder.showExceptionDialog(ex, "Fehler", "Internes Schreibproblem!",
                        "Bitte die Werte der PHP-Konfigurationsdatei unter \"" + pfad
                                + "\\htdocs\\AndroidConnectorAppHTTPScripts\\Konfiguration.php" + "\" berprfen."
                                + "ndern Sie ggf. die Konfiguration mit einem Texteditor!",
                        false);
            }
        }
    }

    /**
     * Stoppt XAMPP als Kommandozeilenanwendung. Nur unter Windows mglich. Wenn
     * nicht Windows, dann Fehlermeldung.
     *
     * @author Quelle:
     * http://www.textpattern.net/wiki/?title=Using_XAMPP_(Apache-MySQL-PHP-Perl)_for_Windows
     * @param pfad Der Pfad zum XAMPP-Verzeichnis
     */
    public static void xampp_stopp(String pfad) {
        //Aufruf zusammennsetzen: Pfad und Startdatei
        //vorher prfen, ob Windows vorliegt --> geht sonst nicht so
        if (OS.contains("win")) {
            String command = "\"" + pfad + "\\xampp_stop.exe" + "\"";
            try {
                //Prozess anlegen, der die exe startet
                Process p = Runtime.getRuntime().exec(command);
                //InputStream zum Einlesen der Eingabe
                InputStream is = p.getInputStream();
                int c;
                while ((c = is.read()) != -1) {
                    //Kontrollausgabe
                    System.out.print((char) c);
                }
                //Fehler fangen
            } catch (IOException ex) {
                /*(new MySQLConnection(new FXMLDocumentController())).showExceptionDialog(ex, "Fehler", "XAMPP konnte aufgrund eines Fehlers nicht gestoppt werden.",
                    "Bitte geben Sie bei einer Beschwerde an den Entwickler folgende Fehlermeldung an:", false);*/

            }
            //nicht Windows --> Fehler
        } else {
            Alert alert = new Alert(AlertType.WARNING);
            alert.setTitle("Bitte XAMPP beenden!");
            alert.setHeaderText("Automatisches Ende von XAMPP nicht mglich, bitte manuell starten!");
            alert.setContentText(
                    "Ein automatisches Beenden von XAMPP ist nur unter Windows mglich. Bitte stoppen Sie XAMPP manuell!");
            alert.showAndWait();
        }
    }

    /**
     * ffnet einen DirectoryChooser, der dann den Installationsordner von XAMPP
     * erfragt.
     *
     * @param event
     */
    @FXML
    private void such_xampp(ActionEvent event) {
        //DirectoryChooser anlegen, der nur Ordner auswhlt
        DirectoryChooser chooser = new DirectoryChooser();
        //Titel und Startverzeichnis setzen
        chooser.setTitle("Installationsordner von XAMPP: ");
        File defaultDirectory = new File(System.getProperty("user.home"));
        chooser.setInitialDirectory(defaultDirectory);
        //Abfrage durchfhren, die einen Ordner liefert. Muss an das Fenster, das ihn gestartet hat, angeheftet sein
        File selectedDirectory = chooser.showDialog(primaryStage);
        if (selectedDirectory != null) {
            //Pfad im Textfeld anzeigen, wenn Verzeichnis gewhlt
            pfad_xampp.setText(selectedDirectory.getAbsolutePath());
        }
    }

    /**
     * Liest eine Excel-Datei ein und entnimmt dieser die Werte fr Startnummer,
     * Name und Kategorie. Fgt die Werte anschlieend der Tabelle hinzu.
     *
     * @param event Klick auf Button, der die Methode aufruft
     */
    @FXML
    private void readExcel(ActionEvent event) {
        //Rcksetzen der Tabelle
        tabelle.setItems(FXCollections.observableArrayList());
        personData = tabelle.getItems();
        //FileChooser einsetzen, um Starterplan einzulesen
        FileChooser fc = new FileChooser();
        //standardmig im Home-Verzeichnis starten
        fc.setInitialDirectory(new File(System.getProperty("user.home")));
        //Alle-Dateien-Filter entfernen
        fc.setSelectedExtensionFilter(null);
        //FileFilter fr Exceldateien hinzufgen
        //nur "alte" Excel-Dateien knnen gelesen werden!
        fc.getExtensionFilters()
                .addAll(new FileChooser.ExtensionFilter("Microsoft Excel 1997-2003 Dokument (.xls)", "*.xls"));
        //Dateien einlesen
        File returnVal = fc.showOpenDialog(primaryStage);
        //prfen, ob Datei zurckgegeben --> eine gewhlt; muss aber nicht existieren
        if (returnVal != null) {
            //ExcelReader anlegen, der Datei dann in den Speicher vergewaltigt.
            ExcelReader reader = null;
            /**
             * Speichert, ob es beim Laden Probleme gab. True, wenn alles gut
             * ist, sonst false.
             */
            boolean geladen = true;
            //Versuch, Datei zu laden
            try {
                reader = new ExcelReader(returnVal.getAbsolutePath());
            } catch (IOException ex) {
                //Exception fangen, die durch Ladefehler entsteht, und diese mit ExceptionDialog anzeigen
                new MySQLConnection(null).showExceptionDialog(ex, "Lesefehler",
                        "Datei konnte nicht gelesen werden.",
                        "Die Datei " + returnVal.getAbsolutePath()
                                + " konnte nicht gelesen werden. Bitte geben Sie bei einer Beschwerde an den Entwickler folgende Fehlermeldung an: ",
                        false);
                //Fehler aufgetreten
                geladen = false;
            }
            //prfen, ob Fehler
            if (geladen) {
                //Datei geladen --> ExcelReader nicht null, kann referiert werden
                Workbook wb = reader.getWorkBook();
                //Datei hat nur 1 Tabelle --> direkt die nehmen
                if (wb.getNumberOfSheets() == 1) {
                    reader.setSheet(0);
                } else {
                    //mehrere Tabellen --> User muss whlen, welche er will
                    List<String> items = new ArrayList<>();
                    //Nahmen aller Tabellen in Liste speichern
                    for (int i = 0; i < wb.getNumberOfSheets(); i++) {
                        items.add(wb.getSheetName(i));
                    }
                    //ChoiceDialog anzeigen, der die Auswahl ermglicht
                    ChoiceDialog<String> dialog = new ChoiceDialog<>(items.get(0), items);
                    dialog.setTitle("Tabelle auswhlen");
                    dialog.setHeaderText("Bitte Tabelle auswhlen!");
                    dialog.setContentText("In der bergebenen Datei wurden " + wb.getNumberOfSheets()
                            + " Tabellen gefunden. Bitte whlen Sie die gewnschte Tabelle aus!");

                    Optional<String> result = dialog.showAndWait();
                    // The Java 8 way to get the response value (with lambda expression).
                    /**
                     * Finale Kopie des readers, der von Lambda adressiert
                     * werden kann.
                     */
                    final ExcelReader temp = reader;
                    result.ifPresent(letter -> {
                        //Tabelle laden, die gewhlt wurde.
                        temp.setSheet(wb.getSheet(letter));
                    });
                    //nderungen bernehmen
                    reader = temp;
                }
                //erste drei Spalten durchgehen, erste Zeile berspringen (Beschriftung), danach erste Spalte als Startnummer, zweite als Nahme, dritte als Kategorie interpretieren
                for (int i = 1; i < reader.getSheet().getLastRowNum() + 1; i++) {
                    //Person-Objekt bilden, das Werte der Person in den Zeilen enthlt
                    //Startnummern werden als Doubles gelesen, Umwandlung in int
                    Person e = new Person((new Double(reader.getCellValueAt(i, 0))).intValue() + "",
                            reader.getCellValueAt(i, 1), reader.getCellValueAt(i, 2));
                    //Starter den Personen hinzufgen ...
                    personData.add(e);
                    //... und ihre Zahl erhhen
                    starter++;
                }
                //genderte Starter in Tabelle bernehmen
                tabelle.setItems(personData);
            }
        }
    }

    /**
     * Aktion beim Klick auf den mit "Abbrechen" beschrifteten Button. Schliet
     * das Fenster einfach.
     *
     * @param event der Klick
     */
    @FXML
    private void abort(ActionEvent event) {
        //Hauptklasse alle Konfigurationswerte bergeben, um die DB in jedem Fall leeren zu knnen
        xampp = pfad_xampp.getText();
        xampp_start(xampp);
        Android_Connector.host = host.getText();
        Android_Connector.port = port.getText();
        Android_Connector.db = db.getText();
        Android_Connector.user = user.getText();
        Android_Connector.pw = pw.getText();
        primaryStage.close();
    }

    /**
     * Ermittelt alle in der Tabelle angegebenen Kategorien. Dabei werden
     * gleiche Angaben jeweils als Kategorie gelesen.
     *
     * @return List of String, welche die Namen aller Kategorien enthlt
     */
    public List<String> getKategorien() {
        //Durchgehen aller Starter, hinzufgen zur Liste, Rckgabe der Liste
        List<String> ret = new ArrayList<>();
        for (Person e : personData) {
            if (!ret.contains(e.getKategorie())) {
                ret.add(e.getKategorie());
            }
        }
        return ret;
    }

    List<String> kategorien = new ArrayList<>();

    /**
     * Gibt die ausgewhlte Kategorie zurck, die jetzt starten soll
     *
     * @return gewhlte Kategorie, wenn noch mehrere Kategorien verfgbar sind,
     * sonst die erste Kategorie, wenn noch eine da ist, sonst null
     */
    public String getSelectedKategorie() {
        /**
         * Was das Programm tun soll.
         */
        final String startmod = startmodus.getSelectionModel().getSelectedItem();
        if (startmod.equals(nach_Kategorien)) {
            //Ermitteln der Kategorien beim ersten Fensteraufruf --> danach nicht mehr, um keinen Doppelstart zu ermglichen
            List<String> items = getKategorien();
            //Prfen, ob das nicht der erste Aufruf des Fensters ist und keine Kategorien mehr offen sind
            if (kategorien.isEmpty() && alteWertebernommen) {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setTitle("Veranstaltung beendet");
                alert.setHeaderText("Alle Lufe aller Kategorien sind durchgefhrt worden.");
                alert.setContentText(
                        "Alle Lufe wurden von den eingeteilten Startern absolviert, die Werte sind gespeichert. Sie werden zur Auswertung umgeleitet!");
                alert.showAndWait();
                printResults(true);
                return null;
            }
            if (kategorien.isEmpty()) {
                kategorien = items;
            }
            //temporre Kopie anlegen
            items = kategorien;
            String ret = null;

            //keine Kategorien mehr da --> Auswertung
            //hier prfen, da spter 1 Wert gelscht wird
            //ChoiceDialog anzeigen, der die Auswahl ermglicht wenn mahrere mgliche Kategorien noch nicht bearbeitet sind
            if (items.size() >= 1) {
                ChoiceDialog<String> dialog = new ChoiceDialog<>(items.get(0), items);
                dialog.setTitle("Kategorie?");
                dialog.setHeaderText("Bitte Kategorie auswhlen!");
                dialog.setContentText(
                        "Bitte whlen Sie aus der folgenden Liste die Kategorie aus, die starten soll!");

                Optional<String> result = dialog.showAndWait();
                // The Java 8 way to get the response value (with lambda expression).
                final StringBuilder tmp = new StringBuilder();
                result.ifPresent(letter -> {
                    //Tabelle laden, die gewhlt wurde.
                    tmp.append(letter);
                });
                ret = tmp.toString();
                //Entfernen der gewhlten Kategorie --> kann nur einmal aufgerufen werden
                kategorien.remove(ret);
                //nur noch eine Kategorie brig --> Auswahl dieser
            } else if (!items.isEmpty()) {
                ret = items.get(0);
                kategorien.remove(ret);
            }
            return ret;
        } else {
            if (startmod.equals(alle_hintereinander) && alteWertebernommen) {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setTitle("Veranstaltung beendet");
                alert.setHeaderText("Alle Lufe aller Kategorien sind durchgefhrt worden.");
                alert.setContentText(
                        "Alle Lufe wurden von den eingeteilten Startern absolviert, die Werte sind gespeichert. Sie werden zur Auswertung umgeleitet!");
                alert.showAndWait();
                printResults(true);
                return null;
            }
            return null;
        }
    }

    /**
     * Speichert alle Werte, die ntig sind, um das Fenster nach einem Absturz
     * wiederherstellen zu knnen.
     *
     * @param pfad Pfad, in dem die Sicherungskopien landen
     * @param aktuelle_Kategorie Kategorie, die gerade gestartet wurde
     */
    public void writeConfig(String pfad, String aktuelle_Kategorie) {
        /**
         * Inhalt der Konfigurations-Datei. Diese speichert alle wichtigen
         * Konfigurationseinstellungen (dem String beim Anlegen bereits
         * bergeben), die Kategorien nebst aktuell gestarteter Kategorie und
         * alle Starter mit Startnummern und Kategorie.
         */
        String save = "Messstationen:" + messstationen.getText() + "\n" + "Messtore:" + messtore.getText() + "\n"
                + "Host:" + host.getText() + "\n" + "Port:" + port.getText() + "\n" + "DB:" + db.getText() + "\n"
                + "clearDB:" + db_leeren.isSelected() + "\n" + "User:" + user.getText() + "\n" + "Passwort:"
                + pw.getText() + "\n" + "xampp:" + pfad_xampp.getText() + "\n" + "checkNetwork:"
                + checkNetwork.isSelected() + "\n" + "Sicherung:" + protokoll.isSelected() + "\n" + "Protokollort:"
                + this.pfad.getText() + "\n" + "aktuelle Kategorie:" + aktuelle_Kategorie + "\n";
        //alle verbleibenden Kategorien durchgehen und anhngen
        for (String kategorie : kategorien) {
            save += "Kategorie:" + kategorie + "\n";
        }
        //alle Werte der Starter ermitteln und druch | getrennt anhngen
        String[][] tableValues = getAllWerte();
        for (String[] tableRow : tableValues) {
            save += "Starter:" + tableRow[0] + "|" + tableRow[1] + "|" + tableRow[2] + "\n";
        }
        //in Protokollordner speichern, wenn ein solcher spezifiziert
        if (pfad != null && !pfad.isEmpty()) {
            //Dateiname ist "Config"
            File file = new File(pfad + "\\Config.txt");
            while (file.exists()) {
                //Datei existiert --> fragen, ob berschrieben werden soll
                int dec = JOptionPane.showConfirmDialog(null,
                        "Datei " + file.getAbsolutePath() + " existiert bereits. Mchten Sie sie berschreiben?",
                        "Datei existiert", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
                //sonst: neuen Dateinamen einlesen, Datei zuweisen, erneut prfen
                if (dec != JOptionPane.YES_OPTION) {
                    String name = JOptionPane.showInputDialog(null,
                            "Bitte geben Sie einen anderen Dateinamen (keinen Pfad) ein!", "neuer Dateiname",
                            JOptionPane.OK_OPTION);
                    file = new File(pfad + "\\" + name + ".txt");
                } //wenn ja: Schleife einfach verlassen
                else {
                    break;
                }
            }
            //Schreiber der Datei
            PrintWriter pWriter = null;
            //Datei schreiben
            try {
                pWriter = new PrintWriter(new BufferedWriter(new FileWriter(file)));
                pWriter.println(save);
                //Fehler fangen, Fehlermeldungen ausgeben
            } catch (IOException ioe) {
                MySQLConnection.staticExceptionDialog(ioe, "Fehler", "Schreibfehler",
                        "Die Datei konnte nicht geschrieben werden!");
            } //sauberes Beenden
            finally {
                if (pWriter != null) {
                    pWriter.flush();
                    pWriter.close();
                }
            }
        }
    }

    @FXML
    private void restore(ActionEvent event) {
        //DirectoryChooser anlegen, der nur Ordner auswhlt
        DirectoryChooser chooser = new DirectoryChooser();
        //Titel und Startverzeichnis setzen
        chooser.setTitle("Speicherort der Protokolldateien: ");
        File defaultDirectory = new File(System.getProperty("user.home"));
        chooser.setInitialDirectory(defaultDirectory);
        //Abfrage durchfhren, die einen Ordner liefert. Muss an das Fenster, das ihn gestartet hat, angeheftet sein
        File selectedDirectory = chooser.showDialog(primaryStage);
        String aktuelleKategorie = "";
        kategorien = new ArrayList<>();
        if (selectedDirectory != null) {
            //Pfad im Textfeld anzeigen, wenn Verzeichnis gewhlt
            pfad.setText(selectedDirectory.getAbsolutePath());
            try {
                File file = new File(pfad.getText() + "/Config.txt");
                BufferedReader in = new BufferedReader(new FileReader(file));
                personData = FXCollections.observableArrayList(); // TODO
                String line = null;
                while ((line = in.readLine()) != null) {
                    if (line.startsWith("Messstationen:")) {
                        messstationen.setText(line.substring(14, line.length()));
                    }
                    if (line.startsWith("Messtore:")) {
                        messtore.setText(line.substring(9, line.length()));
                    }
                    if (line.startsWith("Host:")) {
                        host.setText(line.substring(5, line.length()));
                    }
                    if (line.startsWith("Port:")) {
                        port.setText(line.substring(5, line.length()));
                    }
                    if (line.startsWith("DB:")) {
                        db.setText(line.substring(3, line.length()));
                    }
                    if (line.startsWith("clearDB:")) {
                        db_leeren.setSelected(Boolean.parseBoolean(line.substring(8, line.length())));
                    }
                    if (line.startsWith("User:")) {
                        user.setText(line.substring(5, line.length()));
                    }
                    if (line.startsWith("Passwort:")) {
                        pw.setText(line.substring(9, line.length()));
                    }
                    if (line.startsWith("xampp:")) {
                        pfad_xampp.setText(line.substring(6, line.length()));
                    }
                    if (line.startsWith("checkNetwork:")) {
                        checkNetwork.setSelected(Boolean.parseBoolean(line.substring(13, line.length())));
                    }
                    if (line.startsWith("Sicherung:")) {
                        protokoll.setSelected(Boolean.parseBoolean(line.substring(10, line.length())));
                        info_Ort.setVisible(true);
                        pfad.setVisible(true);
                        pfadSuche.setVisible(true);
                    }
                    if (line.startsWith("Protokollort:")) {
                        pfad.setText(line.substring(13, line.length()));
                    }
                    if (line.startsWith("aktuelle Kategorie:")) {
                        aktuelleKategorie = line.substring(19, line.length());
                        kategorien.add(aktuelleKategorie);
                    }
                    if (line.startsWith("Kategorie:")) {
                        kategorien.add(line.substring(10, line.length()));
                    }
                    if (line.startsWith("Starter:")) {
                        line = line.substring(8, line.length());

                        //Standard-Person anlegen
                        String sn, name, kategorie;
                        sn = line.substring(0, line.indexOf("|"));
                        line = line.substring(line.indexOf("|") + 1);
                        name = line.substring(0, line.indexOf("|"));
                        line = line.substring(line.indexOf("|") + 1);
                        kategorie = line;
                        Person e = new Person(sn, name, kategorie);
                        //zuweisen, welches Attribut von Person die Spalten auslesen sollen
                        //Standardperson den Startern hinzufgen...
                        personData.add(e);
                        //... und ihre Zahl erhhen
                        starter++;
                        tabelle.setItems(personData);
                        refresh_table(tabelle);
                    }
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Speichert die Ergebnisse der Lufe als Excel-Tabelle.
     *
     * @author Quelle:
     * http://viralpatel.net/blogs/java-read-write-excel-file-apache-poi/
     * @param programmende True, wenn das Programm beendet werden soll, sonst
     * false.
     *
     */
    public void printResults(boolean programmende) {
        /**
         * Zahl der Messtore.
         */
        int tore = Integer.parseInt(messtore.getText());
        /**
         * Gibt die gespeicherten Werte der DB an. Jedes Unterarray
         * reprsentiert einen Datensatz. Der zweite Index ist bei 0 die
         * Startnummer, 1 der Name, 2 die Kategorie, 3 Lauf 1, 4 Lauf 2.
         */
        String[][] werte = (new MySQLConnection(host.getText(), port.getText(), db.getText(), user.getText(),
                pw.getText(), null)).getAuswertung(starter, tore);
        //FileChooser einsetzen, um Speicherort zu ermitteln
        FileChooser fc = new FileChooser();
        //Titel anzeigen
        fc.setTitle("Speicherort fr die Auswertung:");
        //standardmig im Home-Verzeichnis starten
        fc.setInitialDirectory(new File(System.getProperty("user.home")));
        //Alle-Dateien-Filter entfernen
        fc.setSelectedExtensionFilter(null);
        //FileFilter fr Exceldateien hinzufgen
        //nur "alte" Excel-Dateien knnen geschrieben werden!
        fc.getExtensionFilters()
                .addAll(new FileChooser.ExtensionFilter("Microsoft Excel 1997-2003 Dokument (.xls)", "*.xls"));
        //Dateien einlesen
        File returnVal = fc.showSaveDialog(primaryStage);
        //prfen, ob Datei zurckgegeben --> eine gewhlt; muss aber nicht existieren
        if (returnVal != null) {
            /**
             * Reprsentation der Excel-Datei.
             */
            HSSFWorkbook workbook = new HSSFWorkbook();
            /**
             * Alle Kategorien, die angegeben sind.
             */
            List<String> kategorien = getKategorien();
            //alle Kategorien durchgehen, fr alle eine Mappe der DB fllen.
            for (String kategorie : kategorien) {
                /**
                 * Reprsentiert die Mappe, in der die Daten landen.
                 */
                HSSFSheet sheet;
                //Prfen, ob die Lnge des Kategorienamens zwischen 1 und 31 liegt --> sonst ungltig
                if (!kategorie.isEmpty() && kategorie.length() < 31) {
                    //wenn ja: Kategoriename ist Mappenname
                    sheet = workbook.createSheet(kategorie);
                    //sonst: Standardname
                } else {
                    sheet = workbook.createSheet("namenlose Kategorie");
                }

                Map<String, List<String>> data = new HashMap<String, List<String>>();
                List<String> label = new ArrayList<>();
                label.add("Startnummer");
                label.add("Name");
                label.add("Kategorie");
                label.add("reine Laufzeit- Lauf 1");
                label.add("Gesamtstrafen- Lauf 1");
                label.add("Laufzeit insgesamt- Lauf 1");
                for (int i = 0; i < tore; i++) {
                    label.add("Strafe Tor " + (i + 1));
                }
                label.add("reine Laufzeit- Lauf 2");
                label.add("Gesamtstrafen- Lauf 2");
                label.add("Laufzeit insgesamt- Lauf 2");
                for (int i = 0; i < tore; i++) {
                    label.add("Strafe Tor " + (i + 1));
                }
                data.put("0", label);

                for (int i = 0; i < werte.length; i++) {
                    //data.put(""+(i+2), werte[i]);
                    if (werte[i][2] != null) {
                        if (werte[i][2].equals(kategorie)) {
                            List<String> angaben = new ArrayList<>();
                            angaben.add(werte[i][0]);
                            angaben.add(werte[i][1]);
                            angaben.add(werte[i][2]);
                            String lauf1 = werte[i][3];
                            if (werte[i][3] != null && !werte[i][3].isEmpty()) {
                                angaben.addAll(extractList(lauf1));
                            }
                            if (werte[i][4] != null && !werte[i][4].isEmpty()) {
                                angaben.addAll(extractList(werte[i][4]));
                            }
                            data.put("" + (i + 1), angaben);
                        }
                    }
                }
                Set<String> keyset = data.keySet();
                int rownum = 0;
                for (String key : keyset) {
                    Row row = sheet.createRow(rownum++);
                    List<String> objArr = data.get(key);
                    int cellnum = 0;
                    for (Object obj : objArr) {
                        Cell cell = row.createCell(cellnum++);
                        if (obj instanceof Date) {
                            cell.setCellValue((Date) obj);
                        } else if (obj instanceof Boolean) {
                            cell.setCellValue((Boolean) obj);
                        } else if (obj instanceof String) {
                            cell.setCellValue((String) obj);
                        } else if (obj instanceof Double) {
                            cell.setCellValue((Double) obj);
                        }
                    }
                }
                for (int i = 0; i < 9 + 2 * tore; i++) {
                    sheet.autoSizeColumn(i);
                }
            }
            try {
                FileOutputStream out = new FileOutputStream(returnVal);
                workbook.write(out);
                out.close();
                System.out.println("Excel written successfully..");
                out = new FileOutputStream(System.getProperty("user.home") + "/Kanu-s.a.M.-Notfall.xls");
                workbook.write(out);
                out.close();

            } catch (FileNotFoundException e) {
                MySQLConnection.staticExceptionDialog(e, "Schreibfehler",
                        "Auswertung konnte nicht geschrieben werden",
                        "Die Excel-Datei mit der Auswertung konnte nicht geschrieben werden. Bitte versuchen Sie es erneut!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (programmende) {
            Alert alert = new Alert(AlertType.INFORMATION);
            alert.setTitle("Programmende");
            alert.setHeaderText("Der Programmablauf ist beendet.");
            alert.setContentText(
                    "Das Programm hat seine Aufgabe erfllt und wird nun beendet. Vielen Dank fr die Benutzung des Softwaresystems!");
            alert.showAndWait();
            Platform.exit();
        }
    }

    /**
     * Verwandelt einen bergebenen String im Format "Laufzeit
     * pur|Strafen|Laufzeit gesamt|Tor: 0|Strafe: 0|usw. in eine Liste um, die
     * als Tabellenzeile funktioniert.
     *
     * @param lauf1 String im beschriebenen Format.
     * @return Liste, an deren erster Position die reine Laufzeit, der zweiten
     * Position die Gesamtstrafen, der dritten Position die Gesamtzeit und
     * danach die
     */
    private List<String> extractList(String lauf1) {
        List<String> ret = new ArrayList<>();
        String[] teile = lauf1.split("\\|");
        ret.add(teile[0]);
        ret.add(teile[1]);
        ret.add(teile[2]);
        for (int i = 3; i <= teile.length - 1; i++) {
            if (teile[i].startsWith("Tor: ")) {
                ret.add(teile[i + 1].substring(8));
            }
        }
        return ret;
    }
}