From 13cad69e1dea27b447d032512b697f9e0fd2f773 Mon Sep 17 00:00:00 2001
From: "J. Neugebauer"
Date: Sun, 11 Dec 2022 17:32:21 +0100
Subject: [PATCH] =?UTF-8?q?Abi-NRW=20Klassen=20zum=20Testen=20eingef=C3=BC?=
=?UTF-8?q?gt?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Die Klassen werden von der Qualitäts- und UnterstützungsAgentur - Landesinstitut für Schule herausgegeben.
---
.../ngb/zm/util/abi/BinarySearchTree.java | 262 +++++++++++++
.../schule/ngb/zm/util/abi/BinaryTree.java | 212 +++++++++++
.../java/schule/ngb/zm/util/abi/Client.java | 163 ++++++++
.../ngb/zm/util/abi/ComparableContent.java | 62 +++
.../schule/ngb/zm/util/abi/Connection.java | 90 +++++
.../ngb/zm/util/abi/DatabaseConnector.java | 151 ++++++++
.../java/schule/ngb/zm/util/abi/Edge.java | 77 ++++
.../java/schule/ngb/zm/util/abi/Graph.java | 314 +++++++++++++++
.../java/schule/ngb/zm/util/abi/List.java | 347 +++++++++++++++++
.../schule/ngb/zm/util/abi/QueryResult.java | 78 ++++
.../java/schule/ngb/zm/util/abi/Queue.java | 144 +++++++
.../java/schule/ngb/zm/util/abi/Server.java | 359 ++++++++++++++++++
.../java/schule/ngb/zm/util/abi/Stack.java | 128 +++++++
.../java/schule/ngb/zm/util/abi/Vertex.java | 53 +++
14 files changed, 2440 insertions(+)
create mode 100755 src/test/java/schule/ngb/zm/util/abi/BinarySearchTree.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/BinaryTree.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/Client.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/ComparableContent.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/Connection.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/DatabaseConnector.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/Edge.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/Graph.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/List.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/QueryResult.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/Queue.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/Server.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/Stack.java
create mode 100755 src/test/java/schule/ngb/zm/util/abi/Vertex.java
diff --git a/src/test/java/schule/ngb/zm/util/abi/BinarySearchTree.java b/src/test/java/schule/ngb/zm/util/abi/BinarySearchTree.java
new file mode 100755
index 0000000..580776a
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/BinarySearchTree.java
@@ -0,0 +1,262 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Generische Klasse BinarySearchTree
+ *
+ *
+ * Mithilfe der generischen Klasse BinarySearchTree koennen beliebig viele
+ * Objekte in einem Binaerbaum (binaerer Suchbaum) entsprechend einer
+ * Ordnungsrelation verwaltet werden.
+ * Ein Objekt der Klasse stellt entweder einen leeren binaeren Suchbaum dar oder
+ * verwaltet ein Inhaltsobjekt sowie einen linken und einen rechten Teilbaum,
+ * die ebenfalls Objekte der Klasse BinarySearchTree sind.
+ * Die Klasse der Objekte, die in dem Suchbaum verwaltet werden sollen, muss
+ * das generische Interface ComparableContent implementieren. Dabei muss durch
+ * Ueberschreiben der drei Vergleichsmethoden isLess, isEqual, isGreater (s.
+ * Dokumentation des Interfaces) eine eindeutige Ordnungsrelation festgelegt
+ * sein.
+ * Alle Objekte im linken Teilbaum sind kleiner als das Inhaltsobjekt des
+ * binaeren Suchbaums. Alle Objekte im rechten Teilbaum sind groesser als das
+ * Inhaltsobjekt des binaeren Suchbaums. Diese Bedingung gilt (rekursiv) auch in
+ * beiden Teilbaeumen.
+ * Hinweis: In dieser Version wird die Klasse BinaryTree nicht benutzt.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Generisch_03 2017-11-28
+ */
+public class BinarySearchTree> {
+
+ /* --------- Anfang der privaten inneren Klasse -------------- */
+
+ /**
+ * Durch diese innere Klasse kann man dafuer sorgen, dass ein leerer Baum
+ * null ist, ein nicht-leerer Baum jedoch immer eine nicht-null-Wurzel sowie
+ * nicht-null-Teilbaeume hat.
+ */
+ private class BSTNode> {
+
+ private CT content;
+ private BinarySearchTree left, right;
+
+ public BSTNode(CT pContent) {
+ // Der Knoten hat einen linken und rechten Teilbaum, die
+ // beide von null verschieden sind. Also hat ein Blatt immer zwei
+ // leere Teilbaeume unter sich.
+ this.content = pContent;
+ left = new BinarySearchTree();
+ right = new BinarySearchTree();
+ }
+
+ }
+
+ /* ----------- Ende der privaten inneren Klasse -------------- */
+
+ private BSTNode node;
+
+ /**
+ * Der Konstruktor erzeugt einen leeren Suchbaum.
+ */
+ public BinarySearchTree() {
+ this.node = null;
+ }
+
+ /**
+ * Diese Anfrage liefert den Wahrheitswert true, wenn der Suchbaum leer ist,
+ * sonst liefert sie den Wert false.
+ *
+ * @return true, wenn der binaere Suchbaum leer ist, sonst false
+ *
+ */
+ public boolean isEmpty() {
+ return this.node == null;
+ }
+
+ /**
+ * Falls der Parameter null ist, geschieht nichts.
+ * Falls ein bezueglich der verwendeten Vergleichsmethode isEqual mit
+ * pContent uebereinstimmendes Objekt im geordneten binaeren Suchbau
+ * enthalten ist, passiert nichts.
+ * Achtung: hier wird davon ausgegangen, dass isEqual genau dann true
+ * liefert, wenn isLess und isGreater false liefern.
+ * Andernfalls (isLess oder isGreater) wird das Objekt pContent entsprechend
+ * der vorgegebenen Ordnungsrelation in den BinarySearchTree eingeordnet.
+ *
+ * @param pContent
+ * einzufuegendes Objekt vom Typ ContentType
+ *
+ */
+ public void insert(ContentType pContent) {
+ if (pContent != null) {
+ if (isEmpty()) {
+ this.node = new BSTNode(pContent);
+ } else if (pContent.isLess(this.node.content)) {
+ this.node.left.insert(pContent);
+ } else if(pContent.isGreater(this.node.content)) {
+ this.node.right.insert(pContent);
+ }
+ }
+ }
+
+ /**
+ * Diese Anfrage liefert den linken Teilbaum des binaeren Suchbaumes.
+ * Wenn er leer ist, wird null zurueckgegeben.
+ *
+ * @return den linken Teilbaum (Objekt vom Typ BinarySearchTree)
+ * bzw. null, wenn der Suchbaum leer ist
+ *
+ */
+ public BinarySearchTree getLeftTree() {
+ if (this.isEmpty()) {
+ return null;
+ } else {
+ return this.node.left;
+ }
+ }
+
+ /**
+ * Diese Anfrage liefert das Inhaltsobjekt des Suchbaumes. Wenn der Suchbaum
+ * leer ist, wird null zurueckgegeben.
+ *
+ * @return das Inhaltsobjekt vom Typ ContentType bzw. null, wenn der aktuelle
+ * Suchbaum leer ist
+ *
+ */
+ public ContentType getContent() {
+ if (this.isEmpty()) {
+ return null;
+ } else {
+ return this.node.content;
+ }
+ }
+
+ /**
+ * Diese Anfrage liefert den rechten Teilbaum des binaeren Suchbaumes.
+ * Wenn er leer ist, wird null zurueckgegeben.
+ *
+ * @return den rechten Teilbaum (Objekt vom Typ BinarySearchTree)
+ * bzw. null, wenn der aktuelle Suchbaum leer ist
+ *
+ */
+ public BinarySearchTree getRightTree() {
+ if (this.isEmpty()) {
+ return null;
+ } else {
+ return this.node.right;
+ }
+ }
+
+ /**
+ * Falls ein bezueglich der verwendeten Vergleichsmethode mit
+ * pContent uebereinstimmendes Objekt im binaeren Suchbaum enthalten
+ * ist, wird dieses entfernt. Falls der Parameter null ist, aendert sich
+ * nichts.
+ *
+ * @param pContent
+ * zu entfernendes Objekt vom Typ ContentType
+ *
+ */
+ public void remove(ContentType pContent) {
+ if (isEmpty() || pContent == null ) {
+ // Abbrechen, da kein Element zum entfernen vorhanden ist.
+ return;
+ }
+
+ if (pContent.isLess(node.content)) {
+ // Element ist im linken Teilbaum zu loeschen.
+ node.left.remove(pContent);
+ } else if (pContent.isGreater(node.content)) {
+ // Element ist im rechten Teilbaum zu loeschen.
+ node.right.remove(pContent);
+ } else {
+ // Element ist gefunden.
+ if (node.left.isEmpty()) {
+ if (node.right.isEmpty()) {
+ // Es gibt keinen Nachfolger.
+ node = null;
+ } else {
+ // Es gibt nur rechts einen Nachfolger.
+ node = getNodeOfRightSuccessor();
+ }
+ } else if (node.right.isEmpty()) {
+ // Es gibt nur links einen Nachfolger.
+ node = getNodeOfLeftSuccessor();
+ } else {
+ // Es gibt links und rechts einen Nachfolger.
+ if (getNodeOfRightSuccessor().left.isEmpty()) {
+ // Der rechte Nachfolger hat keinen linken Nachfolger.
+ node.content = getNodeOfRightSuccessor().content;
+ node.right = getNodeOfRightSuccessor().right;
+ } else {
+ BinarySearchTree previous = node.right
+ .ancestorOfSmallRight();
+ BinarySearchTree smallest = previous.node.left;
+ this.node.content = smallest.node.content;
+ previous.remove(smallest.node.content);
+ }
+ }
+ }
+ }
+
+ /**
+ * Falls ein bezueglich der verwendeten Vergleichsmethode isEqual mit
+ * pContent uebereinstimmendes Objekt im binaeren Suchbaum enthalten ist,
+ * liefert die Anfrage dieses, ansonsten wird null zurueckgegeben.
+ * Falls der Parameter null ist, wird null zurueckgegeben.
+ *
+ * @param pContent
+ * zu suchendes Objekt vom Typ ContentType
+ * @return das gefundene Objekt vom Typ ContentType, bei erfolgloser Suche null
+ *
+ */
+ public ContentType search(ContentType pContent) {
+ if (this.isEmpty() || pContent == null) {
+ // Abbrechen, da es kein Element zu suchen gibt.
+ return null;
+ } else {
+ ContentType content = this.getContent();
+ if (pContent.isLess(content)) {
+ // Element wird im linken Teilbaum gesucht.
+ return this.getLeftTree().search(pContent);
+ } else if (pContent.isGreater(content)) {
+ // Element wird im rechten Teilbaum gesucht.
+ return this.getRightTree().search(pContent);
+ } else if (pContent.isEqual(content)) {
+ // Element wurde gefunden.
+ return content;
+ } else {
+ // Dieser Fall sollte nicht auftreten.
+ return null;
+ }
+ }
+ }
+
+ /* ----------- Weitere private Methoden -------------- */
+
+ /**
+ * Die Methode liefert denjenigen Baum, dessen linker Nachfolger keinen linken
+ * Nachfolger mehr hat. Es ist also spaeter moeglich, in einem Baum im
+ * rechten Nachfolger den Vorgaenger des linkesten Nachfolgers zu finden.
+ *
+ */
+ private BinarySearchTree ancestorOfSmallRight() {
+ if (getNodeOfLeftSuccessor().left.isEmpty()) {
+ return this;
+ } else {
+ return node.left.ancestorOfSmallRight();
+ }
+ }
+
+ private BSTNode getNodeOfLeftSuccessor() {
+ return node.left.node;
+ }
+
+ private BSTNode getNodeOfRightSuccessor() {
+ return node.right.node;
+ }
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/BinaryTree.java b/src/test/java/schule/ngb/zm/util/abi/BinaryTree.java
new file mode 100755
index 0000000..aa293da
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/BinaryTree.java
@@ -0,0 +1,212 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Generische Klasse BinaryTree
+ *
+ *
+ * Mithilfe der generischen Klasse BinaryTree koennen beliebig viele
+ * Inhaltsobjekte vom Typ ContentType in einem Binaerbaum verwaltet werden. Ein
+ * Objekt der Klasse stellt entweder einen leeren Baum dar oder verwaltet ein
+ * Inhaltsobjekt sowie einen linken und einen rechten Teilbaum, die ebenfalls
+ * Objekte der generischen Klasse BinaryTree sind.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Generisch_03 2014-03-01
+ */
+public class BinaryTree {
+
+ /* --------- Anfang der privaten inneren Klasse -------------- */
+
+ /**
+ * Durch diese innere Klasse kann man dafuer sorgen, dass ein leerer Baum
+ * null ist, ein nicht-leerer Baum jedoch immer eine nicht-null-Wurzel sowie
+ * nicht-null-Teilbaeume, ggf. leere Teilbaeume hat.
+ */
+ private class BTNode {
+
+ private CT content;
+ private BinaryTree left, right;
+
+ public BTNode(CT pContent) {
+ // Der Knoten hat einen linken und einen rechten Teilbaum, die
+ // beide von null verschieden sind. Also hat ein Blatt immer zwei
+ // leere Teilbaeume unter sich.
+ this.content = pContent;
+ left = new BinaryTree();
+ right = new BinaryTree();
+ }
+
+ }
+
+ /* ----------- Ende der privaten inneren Klasse -------------- */
+
+ private BTNode node;
+
+ /**
+ * Nach dem Aufruf des Konstruktors existiert ein leerer Binaerbaum.
+ */
+ public BinaryTree() {
+ this.node = null;
+ }
+
+ /**
+ * Wenn der Parameter pContent ungleich null ist, existiert nach dem Aufruf
+ * des Konstruktors der Binaerbaum und hat pContent als Inhaltsobjekt und
+ * zwei leere Teilbaeume. Falls der Parameter null ist, wird ein leerer
+ * Binaerbaum erzeugt.
+ *
+ * @param pContent
+ * das Inhaltsobjekt des Wurzelknotens vom Typ ContentType
+ */
+ public BinaryTree(ContentType pContent) {
+ if (pContent != null) {
+ this.node = new BTNode(pContent);
+ } else {
+ this.node = null;
+ }
+ }
+
+ /**
+ * Wenn der Parameter pContent ungleich null ist, wird ein Binaerbaum mit
+ * pContent als Inhalt und den beiden Teilbaeume pLeftTree und pRightTree
+ * erzeugt. Sind pLeftTree oder pRightTree gleich null, wird der
+ * entsprechende Teilbaum als leerer Binaerbaum eingefuegt. So kann es also
+ * nie passieren, dass linke oder rechte Teilbaeume null sind. Wenn der
+ * Parameter pContent gleich null ist, wird ein leerer Binaerbaum erzeugt.
+ *
+ * @param pContent
+ * das Inhaltsobjekt des Wurzelknotens vom Typ ContentType
+ * @param pLeftTree
+ * der linke Teilbaum vom Typ BinaryTree
+ * @param pRightTree
+ * der rechte Teilbaum vom Typ BinaryTree
+ */
+ public BinaryTree(ContentType pContent, BinaryTree pLeftTree, BinaryTree pRightTree) {
+ if (pContent != null) {
+ this.node = new BTNode(pContent);
+ if (pLeftTree != null) {
+ this.node.left = pLeftTree;
+ } else {
+ this.node.left = new BinaryTree();
+ }
+ if (pRightTree != null) {
+ this.node.right = pRightTree;
+ } else {
+ this.node.right = new BinaryTree();
+ }
+ } else {
+ // Da der Inhalt null ist, wird ein leerer BinarySearchTree erzeugt.
+ this.node = null;
+ }
+ }
+
+ /**
+ * Diese Anfrage liefert den Wahrheitswert true, wenn der Binaerbaum leer
+ * ist, sonst liefert sie den Wert false.
+ *
+ * @return true, wenn der Binaerbaum leer ist, sonst false
+ */
+ public boolean isEmpty() {
+ return this.node == null;
+ }
+
+ /**
+ * Wenn pContent null ist, geschieht nichts.
+ * Ansonsten: Wenn der Binaerbaum leer ist, wird der Parameter pContent als
+ * Inhaltsobjekt sowie ein leerer linker und rechter Teilbaum eingefuegt.
+ * Ist der Binaerbaum nicht leer, wird das Inhaltsobjekt durch pContent
+ * ersetzt. Die Teilbaeume werden nicht geaendert.
+ *
+ * @param pContent
+ * neues Inhaltsobjekt vom Typ ContentType
+ */
+ public void setContent(ContentType pContent) {
+ if (pContent != null) {
+ if (this.isEmpty()) {
+ node = new BTNode(pContent);
+ this.node.left = new BinaryTree();
+ this.node.right = new BinaryTree();
+ }
+ this.node.content = pContent;
+ }
+ }
+
+ /**
+ * Diese Anfrage liefert das Inhaltsobjekt des Binaerbaums. Wenn der
+ * Binaerbaum leer ist, wird null zurueckgegeben.
+ *
+ * @return das Inhaltsobjekt der Wurzel vom Typ ContentType bzw. null, wenn
+ * der Binaerbaum leer ist
+ */
+ public ContentType getContent() {
+ if (this.isEmpty()) {
+ return null;
+ } else {
+ return this.node.content;
+ }
+ }
+
+ /**
+ * Falls der Parameter null ist, geschieht nichts. Wenn der Binaerbaum leer
+ * ist, wird pTree nicht angehaengt. Andernfalls erhaelt der Binaerbaum den
+ * uebergebenen BinaryTree als linken Teilbaum.
+ *
+ * @param pTree
+ * neuer linker Teilbaum vom Typ BinaryTree
+ */
+ public void setLeftTree(BinaryTree pTree) {
+ if (!this.isEmpty() && pTree != null) {
+ this.node.left = pTree;
+ }
+ }
+
+ /**
+ * Falls der Parameter null ist, geschieht nichts. Wenn der Binaerbaum leer
+ * ist, wird pTree nicht angehaengt. Andernfalls erhaelt der Binaerbaum den
+ * uebergebenen BinaryTree als rechten Teilbaum.
+ *
+ * @param pTree
+ * neuer linker Teilbaum vom Typ BinaryTree
+ */
+ public void setRightTree(BinaryTree pTree) {
+ if (!this.isEmpty() && pTree != null) {
+ this.node.right = pTree;
+ }
+ }
+
+ /**
+ * Diese Anfrage liefert den linken Teilbaum des Binaerbaumes. Wenn der
+ * Binaerbaum leer ist, wird null zurueckgegeben.
+ *
+ * @return linker Teilbaum vom Typ BinaryTree oder null, wenn
+ * der aktuelle Binaerbaum leer ist
+ */
+ public BinaryTree getLeftTree() {
+ if (!this.isEmpty()) {
+ return this.node.left;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Diese Anfrage liefert den rechten Teilbaum des Binaerbaumes. Wenn der
+ * Binaerbaum (this) leer ist, wird null zurueckgegeben.
+ *
+ * @return rechter Teilbaum vom Typ BinaryTree oder null, wenn
+ * der aktuelle Binaerbaum (this) leer ist
+ */
+ public BinaryTree getRightTree() {
+ if (!this.isEmpty()) {
+ return this.node.right;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/Client.java b/src/test/java/schule/ngb/zm/util/abi/Client.java
new file mode 100755
index 0000000..3a0f80b
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/Client.java
@@ -0,0 +1,163 @@
+package schule.ngb.zm.util.abi;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Klasse Client
+ *
+ *
+ * Objekte von Unterklassen der abstrakten Klasse Client ermoeglichen
+ * Netzwerkverbindungen zu einem Server mittels TCP/IP-Protokoll. Nach
+ * Verbindungsaufbau koennen Zeichenketten (Strings) zum Server gesendet und von
+ * diesem empfangen werden, wobei der Nachrichtenempfang nebenlaeufig geschieht.
+ * Zur Vereinfachung finden Nachrichtenversand und -empfang zeilenweise statt,
+ * d. h., beim Senden einer Zeichenkette wird ein Zeilentrenner ergaenzt und beim
+ * Empfang wird dieser entfernt. Jede empfangene Nachricht wird einer
+ * Ereignisbehandlungsmethode uebergeben, die in Unterklassen implementiert werden
+ * muss. Es findet nur eine rudimentaere Fehlerbehandlung statt, so dass z.B.
+ * Verbindungsabbrueche nicht zu einem Programmabbruch fuehren. Eine einmal
+ * unterbrochene oder getrennte Verbindung kann nicht reaktiviert werden.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version 30.08.2016
+ */
+
+public abstract class Client
+{
+ private MessageHandler messageHandler;
+
+ private class MessageHandler extends Thread
+ {
+ private SocketWrapper socketWrapper;
+ private boolean active;
+
+ private class SocketWrapper
+ {
+ private Socket socket;
+ private BufferedReader fromServer;
+ private PrintWriter toServer;
+
+ public SocketWrapper(String pServerIP, int pServerPort)
+ {
+ try
+ {
+ socket = new Socket(pServerIP, pServerPort);
+ toServer = new PrintWriter(socket.getOutputStream(), true);
+ fromServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ }
+ catch (IOException e)
+ {
+ socket = null;
+ toServer = null;
+ fromServer = null;
+ }
+ }
+
+ public String receive()
+ {
+ if(fromServer != null)
+ try
+ {
+ return fromServer.readLine();
+ }
+ catch (IOException e)
+ {
+ }
+ return(null);
+ }
+
+ public void send(String pMessage)
+ {
+ if(toServer != null)
+ {
+ toServer.println(pMessage);
+ }
+ }
+
+ public void close()
+ {
+ if(socket != null)
+ try
+ {
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ /*
+ * Falls eine Verbindung getrennt werden soll, deren Endpunkt
+ * nicht mehr existiert bzw. ihrerseits bereits beendet worden ist,
+ * geschieht nichts.
+ */
+ }
+ }
+ }
+
+ private MessageHandler(String pServerIP, int pServerPort)
+ {
+ socketWrapper = new SocketWrapper(pServerIP, pServerPort);
+ start();
+ if(socketWrapper.socket != null)
+ active = true;
+ }
+
+ public void run()
+ {
+ String message = null;
+ while (active)
+ {
+ message = socketWrapper.receive();
+ if (message != null)
+ processMessage(message);
+ else
+ close();
+ }
+ }
+
+ private void send(String pMessage)
+ {
+ if(active)
+ socketWrapper.send(pMessage);
+ }
+
+ private void close()
+ {
+ if(active)
+ {
+ active = false;
+ socketWrapper.close();
+ }
+ }
+ }
+
+ public Client(String pServerIP, int pServerPort)
+ {
+ messageHandler = new MessageHandler(pServerIP, pServerPort);
+ }
+
+ public boolean isConnected()
+ {
+ return(messageHandler.active);
+ }
+
+ public void send(String pMessage)
+ {
+ messageHandler.send(pMessage);
+ }
+
+ public void close()
+ {
+ messageHandler.close();
+ }
+
+ public abstract void processMessage(String pMessage);
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/ComparableContent.java b/src/test/java/schule/ngb/zm/util/abi/ComparableContent.java
new file mode 100755
index 0000000..dfdf23c
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/ComparableContent.java
@@ -0,0 +1,62 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Generisches Interface (Schnittstelle) ComparableContent
+ *
+ *
+ *
Das generische Interface ComparableContent legt die Methoden
+ * fest, ueber die Objekte verfuegen muessen, die in einen binaeren Suchbaum
+ * (BinarySearchTree) eingefuegt werden sollen. Die Ordnungsrelation wird in
+ * Klassen, die ComparableContent implementieren durch Ueberschreiben der drei
+ * implizit abstrakten Methoden isGreater, isEqual und isLess festgelegt.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Generisch_02 2014-03-01
+ */
+public interface ComparableContent {
+
+ /**
+ * Wenn festgestellt wird, dass das Objekt, von dem die Methode aufgerufen
+ * wird, bzgl. der gewuenschten Ordnungsrelation groesser als das Objekt
+ * pContent ist, wird true geliefert. Sonst wird false geliefert.
+ *
+ * @param pContent
+ * das mit dem aufrufenden Objekt zu vergleichende Objekt vom
+ * Typ ContentType
+ * @return true, wenn das aufrufende Objekt groesser ist als das Objekt
+ * pContent, sonst false
+ */
+ public boolean isGreater(ContentType pContent);
+
+ /**
+ * Wenn festgestellt wird, dass das Objekt, von dem die Methode aufgerufen
+ * wird, bzgl. der gewuenschten Ordnungsrelation gleich gross wie das Objekt
+ * pContent ist, wird true geliefert. Sonst wird false geliefert.
+ *
+ * @param pContent
+ * das mit dem aufrufenden Objekt zu vergleichende Objekt vom
+ * Typ ContentType
+ * @return true, wenn das aufrufende Objekt gleich gross ist wie das Objekt
+ * pContent, sonst false
+ */
+ public boolean isEqual(ContentType pContent);
+
+ /**
+ * Wenn festgestellt wird, dass das Objekt, von dem die Methode aufgerufen
+ * wird, bzgl. der gewuenschten Ordnungsrelation kleiner als das Objekt
+ * pContent ist, wird true geliefert. Sonst wird false geliefert.
+ *
+ * @param pContent
+ * das mit dem aufrufenden Objekt zu vergleichende Objekt vom
+ * Typ ContentType
+ * @return true, wenn das aufrufende Objekt kleiner ist als das Objekt
+ * pContent, sonst false
+ */
+ public boolean isLess(ContentType pContent);
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/Connection.java b/src/test/java/schule/ngb/zm/util/abi/Connection.java
new file mode 100755
index 0000000..d4c688c
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/Connection.java
@@ -0,0 +1,90 @@
+package schule.ngb.zm.util.abi; /**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Klasse Connection
+ *
+ *
+ * Objekte der Klasse Connection ermoeglichen eine Netzwerkverbindung zu einem
+ * Server mittels TCP/IP-Protokoll. Nach Verbindungsaufbau koennen Zeichenketten
+ * (Strings) zum Server gesendet und von diesem empfangen werden. Zur
+ * Vereinfachung geschieht dies zeilenweise, d. h., beim Senden einer
+ * Zeichenkette wird ein Zeilentrenner ergaenzt und beim Empfang wird dieser
+ * entfernt. Es findet nur eine rudimentaere Fehlerbehandlung statt, so dass z.B.
+ * der Zugriff auf unterbrochene oder bereits getrennte Verbindungen nicht zu
+ * einem Programmabbruch fuehrt. Eine einmal getrennte Verbindung kann nicht
+ * reaktiviert werden.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version 30.08.2016
+ */
+
+import java.net.*;
+import java.io.*;
+
+public class Connection
+{
+ private Socket socket;
+ private BufferedReader fromServer;
+ private PrintWriter toServer;
+
+ public Connection(String pServerIP, int pServerPort)
+ {
+ try
+ {
+ socket = new Socket(pServerIP, pServerPort);
+ toServer = new PrintWriter(socket.getOutputStream(), true);
+ fromServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ }
+ catch (Exception e)
+ {
+ //Erstelle eine nicht-verbundene Instanz von Socket, wenn die avisierte
+ //Verbindung nicht hergestellt werden kann
+ socket = null;
+ toServer = null;
+ fromServer = null;
+ }
+ }
+
+ public String receive()
+ {
+ if(fromServer != null)
+ try
+ {
+ return fromServer.readLine();
+ }
+ catch (IOException e)
+ {
+ }
+ return(null);
+ }
+
+ public void send(String pMessage)
+ {
+ if(toServer != null)
+ {
+ toServer.println(pMessage);
+ }
+ }
+
+ public void close()
+ {
+
+ if(socket != null && !socket.isClosed())
+ try
+ {
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ /*
+ * Falls eine Verbindung geschlossen werden soll, deren Endpunkt nicht
+ * mehr existiert bzw. seinerseits bereits geschlossen worden ist oder
+ * die nicht korrekt instanziiert werden konnte (socket == null), geschieht
+ * nichts.
+ */
+ }
+ }
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/DatabaseConnector.java b/src/test/java/schule/ngb/zm/util/abi/DatabaseConnector.java
new file mode 100755
index 0000000..13c2c69
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/DatabaseConnector.java
@@ -0,0 +1,151 @@
+package schule.ngb.zm.util.abi;
+
+import java.sql.*;
+import java.sql.Connection;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Klasse DatabaseConnector
+ *
+ *
+ * Ein Objekt der Klasse DatabaseConnector ermoeglicht die Abfrage und Manipulation
+ * einer SQLite-Datenbank.
+ * Beim Erzeugen des Objekts wird eine Datenbankverbindung aufgebaut, so dass
+ * anschließend SQL-Anweisungen an diese Datenbank gerichtet werden koennen.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version 2016-01-24
+ */
+public class DatabaseConnector{
+ private Connection connection;
+ private QueryResult currentQueryResult = null;
+ private String message = null;
+
+ /**
+ * Ein Objekt vom Typ DatabaseConnector wird erstellt, und eine Verbindung zur Datenbank
+ * wird aufgebaut. Mit den Parametern pIP und pPort werden die IP-Adresse und die
+ * Port-Nummer uebergeben, unter denen die Datenbank mit Namen pDatabase zu erreichen ist.
+ * Mit den Parametern pUsername und pPassword werden Benutzername und Passwort fuer die
+ * Datenbank uebergeben.
+ */
+ public DatabaseConnector(String pIP, int pPort, String pDatabase, String pUsername, String pPassword){
+ //Eine Impementierung dieser Schnittstelle fuer SQLite ignoriert pID und pPort, da die Datenbank immer lokal ist.
+ //Auch pUsername und pPassword werden nicht verwendet, da SQLite sie nicht unterstuetzt.
+ try {
+ //Laden der Treiberklasse
+ Class.forName("org.sqlite.JDBC");
+
+ //Verbindung herstellen
+ connection = DriverManager.getConnection("jdbc:sqlite:"+pDatabase);
+
+ } catch (Exception e) {
+ message = e.getMessage();
+ }
+ }
+
+ /**
+ * Der Auftrag schickt den im Parameter pSQLStatement enthaltenen SQL-Befehl an die
+ * Datenbank ab.
+ * Handelt es sich bei pSQLStatement um einen SQL-Befehl, der eine Ergebnismenge
+ * liefert, so kann dieses Ergebnis anschließend mit der Methode getCurrentQueryResult
+ * abgerufen werden.
+ */
+ public void executeStatement(String pSQLStatement){
+ //Altes Ergebnis loeschen
+ currentQueryResult = null;
+ message = null;
+
+ try {
+ //Neues Statement erstellen
+ Statement statement = connection.createStatement();
+
+ //SQL Anweisung an die DB schicken.
+ if (statement.execute(pSQLStatement)) { //Fall 1: Es gibt ein Ergebnis
+
+ //Resultset auslesen
+ ResultSet resultset = statement.getResultSet();
+
+ //Spaltenanzahl ermitteln
+ int columnCount = resultset.getMetaData().getColumnCount();
+
+ //Spaltennamen und Spaltentypen in Felder uebertragen
+ String[] resultColumnNames = new String[columnCount];
+ String[] resultColumnTypes = new String[columnCount];
+ for (int i = 0; i < columnCount; i++){
+ resultColumnNames[i] = resultset.getMetaData().getColumnLabel(i+1);
+ resultColumnTypes[i] = resultset.getMetaData().getColumnTypeName(i+1);
+ }
+
+ //Queue fuer die Zeilen der Ergebnistabelle erstellen
+ Queue rows = new Queue();
+
+ //Daten in Queue uebertragen und Zeilen zaehlen
+ int rowCount = 0;
+ while (resultset.next()){
+ String[] resultrow = new String[columnCount];
+ for (int s = 0; s < columnCount; s++){
+ resultrow[s] = resultset.getString(s+1);
+ }
+ rows.enqueue(resultrow);
+ rowCount = rowCount + 1;
+ }
+
+ //Ergebnisfeld erstellen und Zeilen aus Queue uebertragen
+ String[][] resultData = new String[rowCount][columnCount];
+ int j = 0;
+ while (!rows.isEmpty()){
+ resultData[j] = rows.front();
+ rows.dequeue();
+ j = j + 1;
+ }
+
+ //Statement schließen und Ergebnisobjekt erstellen
+ statement.close();
+ currentQueryResult = new QueryResult(resultData, resultColumnNames, resultColumnTypes);
+
+ } else { //Fall 2: Es gibt kein Ergebnis.
+ //Statement ohne Ergebnisobjekt schliessen
+ statement.close();
+ }
+
+ } catch (Exception e) {
+ //Fehlermeldung speichern
+ message = e.getMessage();
+ }
+ }
+
+ /**
+ * Die Anfrage liefert das Ergebnis des letzten mit der Methode executeStatement an
+ * die Datenbank geschickten SQL-Befehls als Ob-jekt vom Typ QueryResult zurueck.
+ * Wurde bisher kein SQL-Befehl abgeschickt oder ergab der letzte Aufruf von
+ * executeStatement keine Ergebnismenge (z.B. bei einem INSERT-Befehl oder einem
+ * Syntaxfehler), so wird null geliefert.
+ */
+ public QueryResult getCurrentQueryResult(){
+ return currentQueryResult;
+ }
+
+ /**
+ * Die Anfrage liefert null oder eine Fehlermeldung, die sich jeweils auf die letzte zuvor ausgefuehrte
+ * Datenbankoperation bezieht.
+ */
+ public String getErrorMessage(){
+ return message;
+ }
+
+ /**
+ * Die Datenbankverbindung wird geschlossen.
+ */
+ public void close(){
+ try{
+ connection.close();
+ } catch (Exception e) {
+ message = e.getMessage();
+ }
+ }
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/Edge.java b/src/test/java/schule/ngb/zm/util/abi/Edge.java
new file mode 100755
index 0000000..6cf7680
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/Edge.java
@@ -0,0 +1,77 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Klasse Edge
+ *
+ *
+ * Die Klasse Edge stellt eine einzelne, ungerichtete Kante eines Graphen dar.
+ * Beim Erstellen werden die beiden durch sie zu verbindenden Knotenobjekte und eine
+ * Gewichtung als double uebergeben. Beide Knotenobjekte koennen abgefragt werden.
+ * Des Weiteren koennen die Gewichtung und eine Markierung gesetzt und abgefragt werden.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Oktober 2015
+ */
+public class Edge{
+ private Vertex[] vertices;
+ private double weight;
+ private boolean mark;
+
+ /**
+ * Ein neues Objekt vom Typ Edge wird erstellt. Die von diesem Objekt
+ * repraesentierte Kante verbindet die Knoten pVertex und pAnotherVertex mit der
+ * Gewichtung pWeight. Ihre Markierung hat den Wert false.
+ */
+ public Edge( Vertex pVertex, Vertex pAnotherVertex, double pWeight){
+ vertices = new Vertex[2];
+ vertices[0] = pVertex;
+ vertices[1] = pAnotherVertex;
+ weight = pWeight;
+ mark = false;
+ }
+
+ /**
+ * Die Anfrage gibt die beiden Knoten, die durch die Kante verbunden werden, als neues Feld vom Typ Vertex zurueck. Das Feld hat
+ * genau zwei Eintraege mit den Indexwerten 0 und 1.
+ */
+ public Vertex[] getVertices(){
+ Vertex[] result = new Vertex[2];
+ result[0] = vertices[0];
+ result[1] = vertices[1];
+ return result;
+ }
+
+ /**
+ * Der Auftrag setzt das Gewicht der Kante auf pWeight.
+ */
+ public void setWeight(double pWeight){
+ weight = pWeight;
+ }
+
+ /**
+ * Die Anfrage liefert das Gewicht der Kante als double.
+ */
+ public double getWeight(){
+ return weight;
+ }
+
+ /**
+ * Der Auftrag setzt die Markierung der Kante auf den Wert pMark.
+ */
+ public void setMark(boolean pMark){
+ mark = pMark;
+ }
+
+ /**
+ * Die Anfrage liefert true, wenn die Markierung der Kante den Wert true hat, ansonsten false.
+ */
+ public boolean isMarked(){
+ return mark;
+ }
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/Graph.java b/src/test/java/schule/ngb/zm/util/abi/Graph.java
new file mode 100755
index 0000000..701fd2c
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/Graph.java
@@ -0,0 +1,314 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Klasse Graph
+ *
+ *
+ * Die Klasse Graph stellt einen ungerichteten, kantengewichteten Graphen dar. Es koennen
+ * Knoten- und Kantenobjekte hinzugefuegt und entfernt, flache Kopien der Knoten- und Kantenlisten
+ * des Graphen angefragt und Markierungen von Knoten und Kanten gesetzt und ueberprueft werden.
+ * Des Weiteren kann eine Liste der Nachbarn eines bestimmten Knoten, eine Liste der inzidenten
+ * Kanten eines bestimmten Knoten und die Kante von einem bestimmten Knoten zu einem
+ * anderen bestimmten Knoten angefragt werden. Abgesehen davon kann abgefragt werden, welches
+ * Knotenobjekt zu einer bestimmten ID gehoert und ob der Graph leer ist.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Oktober 2015
+ */
+public class Graph{
+ private List vertices;
+ private List edges;
+
+ /**
+ * Ein Objekt vom Typ Graph wird erstellt. Der von diesem Objekt
+ * repraesentierte Graph ist leer.
+ */
+ public Graph(){
+ //Leere Listen fuer Knoten und Kanten erstellen.
+ vertices = new List();
+ edges = new List();
+ }
+
+ /**
+ * Die Anfrage liefert eine neue Liste aller Knotenobjekte vom Typ List.
+ */
+ public List getVertices(){
+ //Eine neue Liste mit allen Vertex-Objekten erstellen.
+ List result = new List();
+ vertices.toFirst();
+ while (vertices.hasAccess()){
+ result.append(vertices.getContent());
+ vertices.next();
+ }
+ //Aktuelles Element zum Anfang bewegen.
+ result.toFirst();
+
+ return result;
+ }
+
+ /**
+ * Die Anfrage liefert eine neue Liste aller Kantenobjekte vom Typ List.
+ */
+ public List getEdges(){
+ //Eine neue Liste mit allen Edge-Objekten erstellen.
+ List result = new List();
+ edges.toFirst();
+ while (edges.hasAccess()){
+ result.append(edges.getContent());
+ edges.next();
+ }
+ //Aktuelles Element zum Anfang bewegen.
+ result.toFirst();
+
+ return result;
+ }
+
+ /**
+ * Die Anfrage liefert das Knotenobjekt mit pID als ID. Ist ein solchen Knotenobjekt nicht im Graphen enthalten,
+ * wird null zurueckgeliefert.
+ */
+ public Vertex getVertex( String pID){
+ //Vertex-Objekt mit pID als ID suchen.
+ Vertex result = null;
+ vertices.toFirst();
+ while (vertices.hasAccess() && result == null){
+ if (vertices.getContent().getID().equals(pID)){
+ result = vertices.getContent();
+ }
+ vertices.next();
+ }
+
+ //Objekt zurueckliefern.
+ return result;
+ }
+
+ /**
+ * Der Auftrag fuegt den Knoten pVertex in den Graphen ein, sofern es noch keinen
+ * Knoten mit demselben ID-Eintrag wie pVertex im Graphen gibt und pVertex eine ID ungleich null hat.
+ * Ansonsten passiert nichts.
+ */
+ public void addVertex( Vertex pVertex){
+ //Pruefen, ob der Vertex existiert und eine ID hat.
+ if (pVertex != null && pVertex.getID() != null) {
+ //Pruefen, ob nicht schon ein Vertex mit gleicher ID enthalten ist.
+ boolean freeID = true;
+ vertices.toFirst();
+ while (vertices.hasAccess() && freeID){
+ if (vertices.getContent().getID().equals(pVertex.getID())){
+ freeID = false;
+ }
+ vertices.next();
+ }
+
+ //Wenn die ID noch frei ist, den Vertex einfuegen, sonst nichts tun.
+ if (freeID) {
+ vertices.append(pVertex);
+ }
+ }
+ }
+
+ /**
+ * Der Auftrag fuegt die Kante pEdge in den Graphen ein, sofern beide durch die Kante verbundenen Knoten
+ * im Graphen enthalten sind, nicht identisch sind und noch keine Kante zwischen den Knoten existiert. Ansonsten passiert nichts.
+ */
+ public void addEdge(Edge pEdge){
+ //Pruefen, ob pEdge exisitert.
+ if (pEdge != null){
+ Vertex[] vertexPair = pEdge.getVertices();
+
+ //Einfuegekriterien pruefen.
+ if (vertexPair[0] != null && vertexPair[1] != null &&
+ this.getVertex(vertexPair[0].getID()) == vertexPair[0] &&
+ this.getVertex(vertexPair[1].getID()) == vertexPair[1] &&
+ this.getEdge(vertexPair[0], vertexPair[1]) == null &&
+ vertexPair[0] != vertexPair[1]){
+ //Kante einfuegen.
+ edges.append(pEdge);
+ }
+ }
+ }
+
+ /**
+ * Der Auftrag entfernt den Knoten pVertex aus dem Graphen und loescht alle Kanten, die mit ihm inzident sind.
+ * Ist der Knoten pVertex nicht im Graphen enthalten, passiert nichts.
+ */
+ public void removeVertex( Vertex pVertex){
+ //Inzidente Kanten entfernen.
+ edges.toFirst();
+ while (edges.hasAccess()){
+ Vertex[] akt = edges.getContent().getVertices();
+ if (akt[0] == pVertex || akt[1] == pVertex){
+ edges.remove();
+ } else {
+ edges.next();
+ }
+ }
+
+ //Knoten entfernen
+ vertices.toFirst();
+ while (vertices.hasAccess() && vertices.getContent()!= pVertex){
+ vertices.next();
+ }
+ if (vertices.hasAccess()){
+ vertices.remove();
+ }
+ }
+
+ /**
+ * Der Auftrag entfernt die Kante pEdge aus dem Graphen. Ist die Kante pEdge nicht
+ * im Graphen enthalten, passiert nichts.
+ */
+ public void removeEdge(Edge pEdge){
+ //Kante aus Kantenliste des Graphen entfernen.
+ edges.toFirst();
+ while (edges.hasAccess()){
+ if (edges.getContent() == pEdge){
+ edges.remove();
+ } else {
+ edges.next();
+ }
+ }
+ }
+
+ /**
+ * Der Auftrag setzt die Markierungen aller Knoten des Graphen auf pMark.
+ */
+ public void setAllVertexMarks(boolean pMark){
+ vertices.toFirst();
+ while (vertices.hasAccess()){
+ vertices.getContent().setMark(pMark);
+ vertices.next();
+ }
+ }
+
+ /**
+ * Der Auftrag setzt die Markierungen aller Kanten des Graphen auf pMark.
+ */
+ public void setAllEdgeMarks(boolean pMark){
+ edges.toFirst();
+ while (edges.hasAccess()){
+ edges.getContent().setMark(pMark);
+ edges.next();
+ }
+ }
+
+ /**
+ * Die Anfrage liefert true, wenn alle Knoten des Graphen mit true markiert sind, ansonsten false.
+ */
+ public boolean allVerticesMarked(){
+ boolean result = true;
+ vertices.toFirst();
+ while (vertices.hasAccess()){
+ if (!vertices.getContent().isMarked()){
+ result = false;
+ }
+ vertices.next();
+ }
+ return result;
+ }
+
+ /**
+ * Die Anfrage liefert true, wenn alle Kanten des Graphen mit true markiert sind, ansonsten false.
+ */
+ public boolean allEdgesMarked(){
+ boolean result = true;
+ edges.toFirst();
+ while (edges.hasAccess()){
+ if (!edges.getContent().isMarked()){
+ result = false;
+ }
+ edges.next();
+ }
+ return result;
+ }
+
+ /**
+ * Die Anfrage liefert alle Nachbarn des Knotens pVertex als neue Liste vom Typ List. Hat der Knoten
+ * pVertex keine Nachbarn in diesem Graphen oder ist gar nicht in diesem Graphen enthalten, so
+ * wird eine leere Liste zurueckgeliefert.
+ */
+ public List getNeighbours( Vertex pVertex){
+ List result = new List();
+
+ //Alle Kanten durchlaufen.
+ edges.toFirst();
+ while (edges.hasAccess()){
+
+ //Wenn ein Knoten der Kante pVertex ist, den anderen als Nachbarn in die Ergebnisliste einfuegen.
+ Vertex[] vertexPair = edges.getContent().getVertices();
+ if (vertexPair[0] == pVertex) {
+ result.append(vertexPair[1]);
+ } else {
+ if (vertexPair[1] == pVertex){
+ result.append(vertexPair[0]);
+ }
+ }
+ edges.next();
+ }
+ return result;
+ }
+
+ /**
+ * Die Anfrage liefert eine neue Liste alle inzidenten Kanten zum Knoten pVertex. Hat der Knoten
+ * pVertex keine inzidenten Kanten in diesem Graphen oder ist gar nicht in diesem Graphen enthalten, so
+ * wird eine leere Liste zurueckgeliefert.
+ */
+ public List getEdges( Vertex pVertex){
+ List result = new List();
+
+ //Alle Kanten durchlaufen.
+ edges.toFirst();
+ while (edges.hasAccess()){
+
+ //Wenn ein Knoten der Kante pVertex ist, dann Kante als inzidente Kante in die Ergebnisliste einfuegen.
+ Vertex[] vertexPair = edges.getContent().getVertices();
+ if (vertexPair[0] == pVertex) {
+ result.append(edges.getContent());
+ } else{
+ if (vertexPair[1] == pVertex){
+ result.append(edges.getContent());
+ }
+ }
+ edges.next();
+ }
+ return result;
+ }
+
+ /**
+ * Die Anfrage liefert die Kante, welche die Knoten pVertex und pAnotherVertex verbindet,
+ * als Objekt vom Typ Edge. Ist der Knoten pVertex oder der Knoten pAnotherVertex nicht
+ * im Graphen enthalten oder gibt es keine Kante, die beide Knoten verbindet, so wird null
+ * zurueckgeliefert.
+ */
+ public Edge getEdge( Vertex pVertex, Vertex pAnotherVertex){
+ Edge result = null;
+
+ //Kanten durchsuchen, solange keine passende gefunden wurde.
+ edges.toFirst();
+ while (edges.hasAccess() && result == null){
+
+ //Pruefen, ob die Kante pVertex und pAnotherVertex verbindet.
+ Vertex[] vertexPair = edges.getContent().getVertices();
+ if ((vertexPair[0] == pVertex && vertexPair[1] == pAnotherVertex) ||
+ (vertexPair[0] == pAnotherVertex && vertexPair[1] == pVertex)) {
+ //Kante als Ergebnis merken.
+ result = edges.getContent();
+ }
+ edges.next();
+ }
+ return result;
+ }
+
+ /**
+ * Die Anfrage liefert true, wenn der Graph keine Knoten enthaelt, ansonsten false.
+ */
+ public boolean isEmpty(){
+ return vertices.isEmpty();
+ }
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/List.java b/src/test/java/schule/ngb/zm/util/abi/List.java
new file mode 100755
index 0000000..625b072
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/List.java
@@ -0,0 +1,347 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Generische Klasse List
+ *
+ *
+ * Objekt der generischen Klasse List verwalten beliebig viele linear
+ * angeordnete Objekte vom Typ ContentType. Auf hoechstens ein Listenobjekt,
+ * aktuellesObjekt genannt, kann jeweils zugegriffen werden.
+ * Wenn eine Liste leer ist, vollstaendig durchlaufen wurde oder das aktuelle
+ * Objekt am Ende der Liste geloescht wurde, gibt es kein aktuelles Objekt.
+ * Das erste oder das letzte Objekt einer Liste koennen durch einen Auftrag zum
+ * aktuellen Objekt gemacht werden. Ausserdem kann das dem aktuellen Objekt
+ * folgende Listenobjekt zum neuen aktuellen Objekt werden.
+ * Das aktuelle Objekt kann gelesen, veraendert oder geloescht werden. Ausserdem
+ * kann vor dem aktuellen Objekt ein Listenobjekt eingefuegt werden.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Generisch_06 2015-10-25
+ */
+public class List {
+
+ /* --------- Anfang der privaten inneren Klasse -------------- */
+
+ private class ListNode {
+
+ private ContentType contentObject;
+ private ListNode next;
+
+ /**
+ * Ein neues Objekt wird erschaffen. Der Verweis ist leer.
+ *
+ * @param pContent das Inhaltsobjekt vom Typ ContentType
+ */
+ private ListNode(ContentType pContent) {
+ contentObject = pContent;
+ next = null;
+ }
+
+ /**
+ * Der Inhalt des Knotens wird zurueckgeliefert.
+ *
+ * @return das Inhaltsobjekt des Knotens
+ */
+ public ContentType getContentObject() {
+ return contentObject;
+ }
+
+ /**
+ * Der Inhalt dieses Kontens wird gesetzt.
+ *
+ * @param pContent das Inhaltsobjekt vom Typ ContentType
+ */
+ public void setContentObject(ContentType pContent) {
+ contentObject = pContent;
+ }
+
+ /**
+ * Der Nachfolgeknoten wird zurueckgeliefert.
+ *
+ * @return das Objekt, auf das der aktuelle Verweis zeigt
+ */
+ public ListNode getNextNode() {
+ return this.next;
+ }
+
+ /**
+ * Der Verweis wird auf das Objekt, das als Parameter uebergeben
+ * wird, gesetzt.
+ *
+ * @param pNext der Nachfolger des Knotens
+ */
+ public void setNextNode(ListNode pNext) {
+ this.next = pNext;
+ }
+
+ }
+
+ /* ----------- Ende der privaten inneren Klasse -------------- */
+
+ // erstes Element der Liste
+ ListNode first;
+
+ // letztes Element der Liste
+ ListNode last;
+
+ // aktuelles Element der Liste
+ ListNode current;
+
+ /**
+ * Eine leere Liste wird erzeugt.
+ */
+ public List() {
+ first = null;
+ last = null;
+ current = null;
+ }
+
+ /**
+ * Die Anfrage liefert den Wert true, wenn die Liste keine Objekte enthaelt,
+ * sonst liefert sie den Wert false.
+ *
+ * @return true, wenn die Liste leer ist, sonst false
+ */
+ public boolean isEmpty() {
+ // Die Liste ist leer, wenn es kein erstes Element gibt.
+ return first == null;
+ }
+
+ /**
+ * Die Anfrage liefert den Wert true, wenn es ein aktuelles Objekt gibt,
+ * sonst liefert sie den Wert false.
+ *
+ * @return true, falls Zugriff moeglich, sonst false
+ */
+ public boolean hasAccess() {
+ // Es gibt keinen Zugriff, wenn current auf kein Element verweist.
+ return current != null;
+ }
+
+ /**
+ * Falls die Liste nicht leer ist, es ein aktuelles Objekt gibt und dieses
+ * nicht das letzte Objekt der Liste ist, wird das dem aktuellen Objekt in
+ * der Liste folgende Objekt zum aktuellen Objekt, andernfalls gibt es nach
+ * Ausfuehrung des Auftrags kein aktuelles Objekt, d.h. hasAccess() liefert
+ * den Wert false.
+ */
+ public void next() {
+ if (this.hasAccess()) {
+ current = current.getNextNode();
+ }
+ }
+
+ /**
+ * Falls die Liste nicht leer ist, wird das erste Objekt der Liste aktuelles
+ * Objekt. Ist die Liste leer, geschieht nichts.
+ */
+ public void toFirst() {
+ if (!isEmpty()) {
+ current = first;
+ }
+ }
+
+ /**
+ * Falls die Liste nicht leer ist, wird das letzte Objekt der Liste
+ * aktuelles Objekt. Ist die Liste leer, geschieht nichts.
+ */
+ public void toLast() {
+ if (!isEmpty()) {
+ current = last;
+ }
+ }
+
+ /**
+ * Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das
+ * aktuelle Objekt zurueckgegeben, andernfalls (hasAccess() == false) gibt
+ * die Anfrage den Wert null zurueck.
+ *
+ * @return das aktuelle Objekt (vom Typ ContentType) oder null, wenn es
+ * kein aktuelles Objekt gibt
+ */
+ public ContentType getContent() {
+ if (this.hasAccess()) {
+ return current.getContentObject();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Falls es ein aktuelles Objekt gibt (hasAccess() == true) und pContent
+ * ungleich null ist, wird das aktuelle Objekt durch pContent ersetzt. Sonst
+ * geschieht nichts.
+ *
+ * @param pContent
+ * das zu schreibende Objekt vom Typ ContentType
+ */
+ public void setContent(ContentType pContent) {
+ // Nichts tun, wenn es keinen Inhalt oder kein aktuelles Element gibt.
+ if (pContent != null && this.hasAccess()) {
+ current.setContentObject(pContent);
+ }
+ }
+
+ /**
+ * Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird ein neues
+ * Objekt vor dem aktuellen Objekt in die Liste eingefuegt. Das aktuelle
+ * Objekt bleibt unveraendert.
+ * Wenn die Liste leer ist, wird pContent in die Liste eingefuegt und es
+ * gibt weiterhin kein aktuelles Objekt (hasAccess() == false).
+ * Falls es kein aktuelles Objekt gibt (hasAccess() == false) und die Liste
+ * nicht leer ist oder pContent gleich null ist, geschieht nichts.
+ *
+ * @param pContent
+ * das einzufuegende Objekt vom Typ ContentType
+ */
+ public void insert(ContentType pContent) {
+ if (pContent != null) { // Nichts tun, wenn es keinen Inhalt gibt.
+ if (this.hasAccess()) { // Fall: Es gibt ein aktuelles Element.
+
+ // Neuen Knoten erstellen.
+ ListNode newNode = new ListNode(pContent);
+
+ if (current != first) { // Fall: Nicht an erster Stelle einfuegen.
+ ListNode previous = this.getPrevious(current);
+ newNode.setNextNode(previous.getNextNode());
+ previous.setNextNode(newNode);
+ } else { // Fall: An erster Stelle einfuegen.
+ newNode.setNextNode(first);
+ first = newNode;
+ }
+
+ } else { //Fall: Es gibt kein aktuelles Element.
+
+ if (this.isEmpty()) { // Fall: In leere Liste einfuegen.
+
+ // Neuen Knoten erstellen.
+ ListNode newNode = new ListNode(pContent);
+
+ first = newNode;
+ last = newNode;
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Falls pContent gleich null ist, geschieht nichts.
+ * Ansonsten wird ein neues Objekt pContent am Ende der Liste eingefuegt.
+ * Das aktuelle Objekt bleibt unveraendert.
+ * Wenn die Liste leer ist, wird das Objekt pContent in die Liste eingefuegt
+ * und es gibt weiterhin kein aktuelles Objekt (hasAccess() == false).
+ *
+ * @param pContent
+ * das anzuhaengende Objekt vom Typ ContentType
+ */
+ public void append(ContentType pContent) {
+ if (pContent != null) { // Nichts tun, wenn es keine Inhalt gibt.
+
+ if (this.isEmpty()) { // Fall: An leere Liste anfuegen.
+ this.insert(pContent);
+ } else { // Fall: An nicht-leere Liste anfuegen.
+
+ // Neuen Knoten erstellen.
+ ListNode newNode = new ListNode(pContent);
+
+ last.setNextNode(newNode);
+ last = newNode; // Letzten Knoten aktualisieren.
+ }
+
+ }
+ }
+
+ /**
+ * Falls es sich bei der Liste und pList um dasselbe Objekt handelt,
+ * pList null oder eine leere Liste ist, geschieht nichts.
+ * Ansonsten wird die Liste pList an die aktuelle Liste angehaengt.
+ * Anschliessend wird pList eine leere Liste. Das aktuelle Objekt bleibt
+ * unveraendert. Insbesondere bleibt hasAccess identisch.
+ *
+ * @param pList
+ * die am Ende anzuhaengende Liste vom Typ List
+ */
+ public void concat(List pList) {
+ if (pList != this && pList != null && !pList.isEmpty()) { // Nichts tun,
+ // wenn pList und this identisch, pList leer oder nicht existent.
+
+ if (this.isEmpty()) { // Fall: An leere Liste anfuegen.
+ this.first = pList.first;
+ this.last = pList.last;
+ } else { // Fall: An nicht-leere Liste anfuegen.
+ this.last.setNextNode(pList.first);
+ this.last = pList.last;
+ }
+
+ // Liste pList loeschen.
+ pList.first = null;
+ pList.last = null;
+ pList.current = null;
+ }
+ }
+
+ /**
+ * Wenn die Liste leer ist oder es kein aktuelles Objekt gibt (hasAccess()
+ * == false), geschieht nichts.
+ * Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das
+ * aktuelle Objekt geloescht und das Objekt hinter dem geloeschten Objekt
+ * wird zum aktuellen Objekt.
+ * Wird das Objekt, das am Ende der Liste steht, geloescht, gibt es kein
+ * aktuelles Objekt mehr.
+ */
+ public void remove() {
+ // Nichts tun, wenn es kein aktuelle Element gibt oder die Liste leer ist.
+ if (this.hasAccess() && !this.isEmpty()) {
+
+ if (current == first) {
+ first = first.getNextNode();
+ } else {
+ ListNode previous = this.getPrevious(current);
+ if (current == last) {
+ last = previous;
+ }
+ previous.setNextNode(current.getNextNode());
+ }
+
+ ListNode temp = current.getNextNode();
+ current.setContentObject(null);
+ current.setNextNode(null);
+ current = temp;
+
+ //Beim loeschen des letzten Elements last auf null setzen.
+ if (this.isEmpty()) {
+ last = null;
+ }
+ }
+ }
+
+ /**
+ * Liefert den Vorgaengerknoten des Knotens pNode. Ist die Liste leer, pNode
+ * == null, pNode nicht in der Liste oder pNode der erste Knoten der Liste,
+ * wird null zurueckgegeben.
+ *
+ * @param pNode
+ * der Knoten, dessen Vorgaenger zurueckgegeben werden soll
+ * @return der Vorgaenger des Knotens pNode oder null, falls die Liste leer ist,
+ * pNode == null ist, pNode nicht in der Liste ist oder pNode der erste Knoten
+ * der Liste ist
+ */
+ private ListNode getPrevious(ListNode pNode) {
+ if (pNode != null && pNode != first && !this.isEmpty()) {
+ ListNode temp = first;
+ while (temp != null && temp.getNextNode() != pNode) {
+ temp = temp.getNextNode();
+ }
+ return temp;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/QueryResult.java b/src/test/java/schule/ngb/zm/util/abi/QueryResult.java
new file mode 100755
index 0000000..4f8a127
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/QueryResult.java
@@ -0,0 +1,78 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Klasse QueryResult
+ *
+ *
+ * Ein Objekt der Klasse QueryResult stellt die Ergebnistabelle einer Datenbankanfrage mit Hilfe
+ * der Klasse DatabaseConnector dar. Objekte dieser Klasse werden nur von der Klasse DatabaseConnector erstellt.
+ * Die Klasse verfuegt ueber keinen oeffentlichen Konstruktor.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version 2015-01-31
+ */
+public class QueryResult{
+ private String[][] data;
+ private String[] columnNames;
+ private String[] columnTypes;
+
+ /**
+ * Paketinterner Konstruktor.
+ */
+ QueryResult(String[][] pData, String[] pColumnNames, String[] pColumnTypes){
+ data = pData;
+ columnNames = pColumnNames;
+ columnTypes = pColumnTypes;
+ }
+
+ /**
+ * Die Anfrage liefert die Eintraege der Ergebnistabelle als zweidimensionales Feld
+ * vom Typ String. Der erste Index des Feldes stellt die Zeile und der zweite die
+ * Spalte dar (d.h. Object[zeile][spalte]).
+ */
+ public String[][] getData(){
+ return data;
+ }
+
+ /**
+ * Die Anfrage liefert die Bezeichner der Spalten der Ergebnistabelle als Feld vom
+ * Typ String zurueck.
+ */
+ public String[] getColumnNames(){
+ return columnNames;
+ }
+
+ /**
+ * Die Anfrage liefert die Typenbezeichnung der Spalten der Ergebnistabelle als Feld
+ * vom Typ String zurueck. Die Bezeichnungen entsprechen den Angaben in der MySQL-Datenbank.
+ */
+ public String[] getColumnTypes(){
+ return columnTypes;
+ }
+
+ /**
+ * Die Anfrage liefert die Anzahl der Zeilen der Ergebnistabelle als Integer.
+ */
+ public int getRowCount(){
+ if (data != null )
+ return data.length;
+ else
+ return 0;
+ }
+
+ /**
+ * Die Anfrage liefert die Anzahl der Spalten der Ergebnistabelle als Integer.
+ */
+ public int getColumnCount(){
+ if (data != null && data.length > 0 && data[0] != null)
+ return data[0].length;
+ else
+ return 0;
+ }
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/Queue.java b/src/test/java/schule/ngb/zm/util/abi/Queue.java
new file mode 100755
index 0000000..44b39b5
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/Queue.java
@@ -0,0 +1,144 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Generische Klasse Queue
+ *
+ *
+ * Objekte der generischen Klasse Queue (Warteschlange) verwalten beliebige
+ * Objekte vom Typ ContentType nach dem First-In-First-Out-Prinzip, d.h., das
+ * zuerst abgelegte Objekt wird als erstes wieder entnommen. Alle Methoden haben
+ * eine konstante Laufzeit, unabhaengig von der Anzahl der verwalteten Objekte.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Generisch_02 2014-02-21
+ */
+public class Queue {
+
+ /* --------- Anfang der privaten inneren Klasse -------------- */
+
+ private class QueueNode {
+
+ private ContentType content = null;
+ private QueueNode nextNode = null;
+
+ /**
+ * Ein neues Objekt vom Typ QueueNode wird erschaffen.
+ * Der Inhalt wird per Parameter gesetzt. Der Verweis ist leer.
+ *
+ * @param pContent das Inhaltselement des Knotens vom Typ ContentType
+ */
+ public QueueNode(ContentType pContent) {
+ content = pContent;
+ nextNode = null;
+ }
+
+ /**
+ * Der Verweis wird auf das Objekt, das als Parameter uebergeben wird,
+ * gesetzt.
+ *
+ * @param pNext der Nachfolger des Knotens
+ */
+ public void setNext(QueueNode pNext) {
+ nextNode = pNext;
+ }
+
+ /**
+ * Liefert das naechste Element des aktuellen Knotens.
+ *
+ * @return das Objekt vom Typ QueueNode, auf das der aktuelle Verweis zeigt
+ */
+ public QueueNode getNext() {
+ return nextNode;
+ }
+
+ /**
+ * Liefert das Inhaltsobjekt des Knotens vom Typ ContentType.
+ *
+ * @return das Inhaltsobjekt des Knotens
+ */
+ public ContentType getContent() {
+ return content;
+ }
+
+ }
+
+ /* ----------- Ende der privaten inneren Klasse -------------- */
+
+ private QueueNode head;
+ private QueueNode tail;
+
+ /**
+ * Eine leere Schlange wird erzeugt.
+ * Objekte, die in dieser Schlange verwaltet werden, muessen vom Typ
+ * ContentType sein.
+ */
+ public Queue() {
+ head = null;
+ tail = null;
+ }
+
+ /**
+ * Die Anfrage liefert den Wert true, wenn die Schlange keine Objekte enthaelt,
+ * sonst liefert sie den Wert false.
+ *
+ * @return true, falls die Schlange leer ist, sonst false
+ */
+ public boolean isEmpty() {
+ return head == null;
+ }
+
+ /**
+ * Das Objekt pContentType wird an die Schlange angehaengt.
+ * Falls pContentType gleich null ist, bleibt die Schlange unveraendert.
+ *
+ * @param pContent
+ * das anzuhaengende Objekt vom Typ ContentType
+ */
+ public void enqueue(ContentType pContent) {
+ if (pContent != null) {
+ QueueNode newNode = new QueueNode(pContent);
+ if (this.isEmpty()) {
+ head = newNode;
+ tail = newNode;
+ } else {
+ tail.setNext(newNode);
+ tail = newNode;
+ }
+ }
+ }
+
+ /**
+ * Das erste Objekt wird aus der Schlange entfernt.
+ * Falls die Schlange leer ist, wird sie nicht veraendert.
+ */
+ public void dequeue() {
+ if (!this.isEmpty()) {
+ head = head.getNext();
+ if (this.isEmpty()) {
+ head = null;
+ tail = null;
+ }
+ }
+ }
+
+ /**
+ * Die Anfrage liefert das erste Objekt der Schlange.
+ * Die Schlange bleibt unveraendert.
+ * Falls die Schlange leer ist, wird null zurueckgegeben.
+ *
+ * @return das erste Objekt der Schlange vom Typ ContentType oder null,
+ * falls die Schlange leer ist
+ */
+ public ContentType front() {
+ if (this.isEmpty()) {
+ return null;
+ } else {
+ return head.getContent();
+ }
+ }
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/Server.java b/src/test/java/schule/ngb/zm/util/abi/Server.java
new file mode 100755
index 0000000..99aaf96
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/Server.java
@@ -0,0 +1,359 @@
+package schule.ngb.zm.util.abi; /**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Klasse Server
+ *
+ *
+ * Objekte von Unterklassen der abstrakten Klasse Server ermoeglichen das
+ * Anbieten von Serverdiensten, so dass Clients Verbindungen zum Server mittels
+ * TCP/IP-Protokoll aufbauen koennen. Zur Vereinfachung finden Nachrichtenversand
+ * und -empfang zeilenweise statt, d. h., beim Senden einer Zeichenkette wird ein
+ * Zeilentrenner ergaenzt und beim Empfang wird dieser entfernt.
+ * Verbindungsannahme, Nachrichtenempfang und Verbindungsende geschehen
+ * nebenlaeufig. Auf diese Ereignisse muss durch Ueberschreiben der entsprechenden
+ * Ereignisbehandlungsmethoden reagiert werden. Es findet nur eine rudimentaere
+ * Fehlerbehandlung statt, so dass z.B. Verbindungsabbrueche nicht zu einem
+ * Programmabbruch fuehren. Einmal unterbrochene oder getrennte Verbindungen
+ * koennen nicht reaktiviert werden.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version 30.08.2016
+ */
+import java.net.*;
+import java.io.*;
+
+public abstract class Server
+{
+ private NewConnectionHandler connectionHandler;
+ private List messageHandlers;
+
+ private class NewConnectionHandler extends Thread
+ {
+ private ServerSocket serverSocket;
+ private boolean active;
+
+ public NewConnectionHandler(int pPort)
+ {
+ try
+ {
+ serverSocket = new ServerSocket(pPort);
+ start();
+ active = true;
+ }
+ catch (Exception e)
+ {
+ serverSocket = null;
+ active = false;
+ }
+ }
+
+ public void run()
+ {
+ while (active)
+ {
+ try
+ {
+ //Warten auf Verbdinungsversuch durch Client:
+ Socket clientSocket = serverSocket.accept();
+ // Eingehende Nachrichten vom neu verbundenen Client werden
+ // in einem eigenen Thread empfangen:
+ addNewClientMessageHandler(clientSocket);
+ processNewConnection(clientSocket.getInetAddress().getHostAddress(),clientSocket.getPort());
+ }
+
+ catch (IOException e)
+ {
+ /*
+ * Kann keine Verbindung zum anfragenden Client aufgebaut werden,
+ * geschieht nichts.
+ */
+ }
+ }
+ }
+
+ public void close()
+ {
+ active = false;
+ if(serverSocket != null)
+ try
+ {
+ serverSocket.close();
+ }
+ catch (IOException e)
+ {
+ /*
+ * Befindet sich der ServerSocket im accept()-Wartezustand oder wurde
+ * er bereits geschlossen, geschieht nichts.
+ */
+ }
+ }
+ }
+
+ private class ClientMessageHandler extends Thread
+ {
+ private ClientSocketWrapper socketWrapper;
+ private boolean active;
+
+ private class ClientSocketWrapper
+ {
+ private Socket clientSocket;
+ private BufferedReader fromClient;
+ private PrintWriter toClient;
+
+ public ClientSocketWrapper(Socket pSocket)
+ {
+ try
+ {
+ clientSocket = pSocket;
+ toClient = new PrintWriter(clientSocket.getOutputStream(), true);
+ fromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+ }
+ catch (IOException e)
+ {
+ clientSocket = null;
+ toClient = null;
+ fromClient = null;
+ }
+ }
+
+ public String receive()
+ {
+ if(fromClient != null)
+ try
+ {
+ return fromClient.readLine();
+ }
+ catch (IOException e)
+ {
+ }
+ return(null);
+ }
+
+ public void send(String pMessage)
+ {
+ if(toClient != null)
+ {
+ toClient.println(pMessage);
+ }
+ }
+
+ public String getClientIP()
+ {
+ if(clientSocket != null)
+ return(clientSocket.getInetAddress().getHostAddress());
+ else
+ return(null); //Gemaess Java-API Rueckgabe bei nicht-verbundenen Sockets
+ }
+
+ public int getClientPort()
+ {
+ if(clientSocket != null)
+ return(clientSocket.getPort());
+ else
+ return(0); //Gemaess Java-API Rueckgabe bei nicht-verbundenen Sockets
+ }
+
+ public void close()
+ {
+ if(clientSocket != null)
+ try
+ {
+ clientSocket.close();
+ }
+ catch (IOException e)
+ {
+ /*
+ * Falls eine Verbindung getrennt werden soll, deren Endpunkt
+ * nicht mehr existiert bzw. ihrerseits bereits beendet worden ist,
+ * geschieht nichts.
+ */
+ }
+ }
+ }
+
+ private ClientMessageHandler(Socket pClientSocket)
+ {
+ socketWrapper = new ClientSocketWrapper(pClientSocket);
+ if(pClientSocket!=null)
+ {
+ start();
+ active = true;
+ }
+ else
+ {
+ active = false;
+ }
+ }
+
+ public void run()
+ {
+ String message = null;
+ while (active)
+ {
+ message = socketWrapper.receive();
+ if (message != null)
+ processMessage(socketWrapper.getClientIP(), socketWrapper.getClientPort(), message);
+ else
+ {
+ ClientMessageHandler aMessageHandler = findClientMessageHandler(socketWrapper.getClientIP(), socketWrapper.getClientPort());
+ if (aMessageHandler != null)
+ {
+ aMessageHandler.close();
+ removeClientMessageHandler(aMessageHandler);
+ processClosingConnection(socketWrapper.getClientIP(), socketWrapper.getClientPort());
+ }
+ }
+ }
+ }
+
+ public void send(String pMessage)
+ {
+ if(active)
+ socketWrapper.send(pMessage);
+ }
+
+ public void close()
+ {
+ if(active)
+ {
+ active=false;
+ socketWrapper.close();
+ }
+ }
+
+ public String getClientIP()
+ {
+ return(socketWrapper.getClientIP());
+ }
+
+ public int getClientPort()
+ {
+ return(socketWrapper.getClientPort());
+ }
+
+ }
+
+ public Server(int pPort)
+ {
+ connectionHandler = new NewConnectionHandler(pPort);
+ messageHandlers = new List();
+ }
+
+ public boolean isOpen()
+ {
+ return(connectionHandler.active);
+ }
+
+ public boolean isConnectedTo(String pClientIP, int pClientPort)
+ {
+ ClientMessageHandler aMessageHandler = findClientMessageHandler(pClientIP, pClientPort);
+ if (aMessageHandler != null)
+ return(aMessageHandler.active);
+ else
+ return(false);
+ }
+
+ public void send(String pClientIP, int pClientPort, String pMessage)
+ {
+ ClientMessageHandler aMessageHandler = this.findClientMessageHandler(pClientIP, pClientPort);
+ if (aMessageHandler != null)
+ aMessageHandler.send(pMessage);
+ }
+
+ public void sendToAll(String pMessage)
+ {
+ synchronized(messageHandlers)
+ {
+ messageHandlers.toFirst();
+ while (messageHandlers.hasAccess())
+ {
+ messageHandlers.getContent().send(pMessage);
+ messageHandlers.next();
+ }
+ }
+ }
+
+ public void closeConnection(String pClientIP, int pClientPort)
+ {
+ ClientMessageHandler aMessageHandler = findClientMessageHandler(pClientIP, pClientPort);
+ if (aMessageHandler != null)
+ {
+ processClosingConnection(pClientIP, pClientPort);
+ aMessageHandler.close();
+ removeClientMessageHandler(aMessageHandler);
+ }
+
+ }
+
+ public void close()
+ {
+ connectionHandler.close();
+
+ synchronized(messageHandlers)
+ {
+ ClientMessageHandler aMessageHandler;
+ messageHandlers.toFirst();
+ while (messageHandlers.hasAccess())
+ {
+ aMessageHandler = messageHandlers.getContent();
+ processClosingConnection(aMessageHandler.getClientIP(), aMessageHandler.getClientPort());
+ aMessageHandler.close();
+ messageHandlers.remove();
+ }
+ }
+
+ }
+ public abstract void processNewConnection(String pClientIP, int pClientPort);
+
+ public abstract void processMessage(String pClientIP, int pClientPort, String pMessage);
+
+ public abstract void processClosingConnection(String pClientIP, int pClientPort);
+
+ private void addNewClientMessageHandler(Socket pClientSocket)
+ {
+ synchronized(messageHandlers)
+ {
+ messageHandlers.append(new ClientMessageHandler(pClientSocket));
+ }
+ }
+
+ private void removeClientMessageHandler(ClientMessageHandler pClientMessageHandler)
+ {
+ synchronized(messageHandlers)
+ {
+ messageHandlers.toFirst();
+ while (messageHandlers.hasAccess())
+ {
+ if (pClientMessageHandler == messageHandlers.getContent())
+ {
+ messageHandlers.remove();
+ return;
+ }
+ else
+ messageHandlers.next();
+ }
+ }
+ }
+
+ private ClientMessageHandler findClientMessageHandler(String pClientIP, int pClientPort)
+ {
+ synchronized(messageHandlers)
+ {
+ ClientMessageHandler aMessageHandler;
+ messageHandlers.toFirst();
+
+ while (messageHandlers.hasAccess())
+ {
+ aMessageHandler = messageHandlers.getContent();
+ if (aMessageHandler.getClientIP().equals(pClientIP) && aMessageHandler.getClientPort() == pClientPort)
+ return (aMessageHandler);
+ messageHandlers.next();
+ }
+ return (null);
+ }
+ }
+
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/Stack.java b/src/test/java/schule/ngb/zm/util/abi/Stack.java
new file mode 100755
index 0000000..7d9cccb
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/Stack.java
@@ -0,0 +1,128 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Generische Klasse Stack
+ *
+ *
+ * Objekte der generischen Klasse Stack (Keller, Stapel) verwalten beliebige
+ * Objekte vom Typ ContentType nach dem Last-In-First-Out-Prinzip, d.h., das
+ * zuletzt abgelegte Objekt wird als erstes wieder entnommen. Alle Methoden
+ * haben eine konstante Laufzeit, unabhaengig von der Anzahl der verwalteten
+ * Objekte.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Generisch_02 2014-02-21
+ */
+public class Stack {
+
+ /* --------- Anfang der privaten inneren Klasse -------------- */
+
+ private class StackNode {
+
+ private ContentType content = null;
+ private StackNode nextNode = null;
+
+ /**
+ * Ein neues Objekt vom Typ StackNode wird erschaffen.
+ * Der Inhalt wird per Parameter gesetzt. Der Verweis ist leer.
+ *
+ * @param pContent der Inhalt des Knotens
+ */
+ public StackNode(ContentType pContent) {
+ content = pContent;
+ nextNode = null;
+ }
+
+ /**
+ * Der Verweis wird auf das Objekt, das als Parameter uebergeben wird,
+ * gesetzt.
+ *
+ * @param pNext der Nachfolger des Knotens
+ */
+ public void setNext(StackNode pNext) {
+ nextNode = pNext;
+ }
+
+ /**
+ *
+ * @return das Objekt, auf das der aktuelle Verweis zeigt
+ */
+ public StackNode getNext() {
+ return nextNode;
+ }
+
+ /**
+ * @return das Inhaltsobjekt vom Typ ContentType
+ */
+ public ContentType getContent() {
+ return content;
+ }
+ }
+
+ /* ----------- Ende der privaten inneren Klasse -------------- */
+
+ private StackNode head;
+
+ /**
+ * Ein leerer Stapel wird erzeugt. Objekte, die in diesem Stapel verwaltet
+ * werden, muessen vom Typ ContentType sein.
+ */
+ public Stack() {
+ head = null;
+ }
+
+ /**
+ * Die Anfrage liefert den Wert true, wenn der Stapel keine Objekte
+ * enthaelt, sonst liefert sie den Wert false.
+ *
+ * @return true, falls der Stapel leer ist, sonst false
+ */
+ public boolean isEmpty() {
+ return (head == null);
+ }
+
+ /**
+ * Das Objekt pContentType wird oben auf den Stapel gelegt. Falls
+ * pContentType gleich null ist, bleibt der Stapel unveraendert.
+ *
+ * @param pContent
+ * das einzufuegende Objekt vom Typ ContentType
+ */
+ public void push(ContentType pContent) {
+ if (pContent != null) {
+ StackNode node = new StackNode(pContent);
+ node.setNext(head);
+ head = node;
+ }
+ }
+
+ /**
+ * Das zuletzt eingefuegte Objekt wird von dem Stapel entfernt. Falls der
+ * Stapel leer ist, bleibt er unveraendert.
+ */
+ public void pop() {
+ if (!isEmpty()) {
+ head = head.getNext();
+ }
+ }
+
+ /**
+ * Die Anfrage liefert das oberste Stapelobjekt. Der Stapel bleibt
+ * unveraendert. Falls der Stapel leer ist, wird null zurueckgegeben.
+ *
+ * @return das oberste Stackelement vom Typ ContentType oder null, falls
+ * der Stack leer ist
+ */
+ public ContentType top() {
+ if (!this.isEmpty()) {
+ return head.getContent();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/schule/ngb/zm/util/abi/Vertex.java b/src/test/java/schule/ngb/zm/util/abi/Vertex.java
new file mode 100755
index 0000000..66c6937
--- /dev/null
+++ b/src/test/java/schule/ngb/zm/util/abi/Vertex.java
@@ -0,0 +1,53 @@
+package schule.ngb.zm.util.abi;
+
+/**
+ *
+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
+ *
+ *
+ * Klasse Vertex
+ *
+ *
+ * Die Klasse Vertex stellt einen einzelnen Knoten eines Graphen dar. Jedes Objekt
+ * dieser Klasse verfuegt ueber eine im Graphen eindeutige ID als String und kann diese
+ * ID zurueckliefern. Darueber hinaus kann eine Markierung gesetzt und abgefragt werden.
+ *
+ *
+ * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
+ * @version Oktober 2015
+ */
+public class Vertex{
+ //Einmalige ID des Knotens und Markierung
+ private String id;
+ private boolean mark;
+
+ /**
+ * Ein neues Objekt vom Typ Vertex wird erstellt. Seine Markierung hat den Wert false.
+ */
+ public Vertex(String pID){
+ id = pID;
+ mark = false;
+ }
+
+ /**
+ * Die Anfrage liefert die ID des Knotens als String.
+ */
+ public String getID(){
+ return new String(id);
+ }
+
+ /**
+ * Der Auftrag setzt die Markierung des Knotens auf den Wert pMark.
+ */
+ public void setMark(boolean pMark){
+ mark = pMark;
+ }
+
+ /**
+ * Die Anfrage liefert true, wenn die Markierung des Knotens den Wert true hat, ansonsten false.
+ */
+ public boolean isMarked(){
+ return mark;
+ }
+
+}