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; + } + +}