diff --git a/pom.xml b/pom.xml
index 1f61f5d..fcca17f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,6 +13,7 @@
5.8.2
10.17.1.0
1.18.30
+ 6.1.2.Final
@@ -141,7 +142,12 @@
org.hibernate.orm
hibernate-core
- 6.1.2.Final
+ ${hibernate.version}
+
+
+ org.hibernate
+ hibernate-c3p0
+ ${hibernate.version}
org.apache.derby
diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExampleService.java b/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExampleService.java
index fc6422b..53cbdd8 100644
--- a/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExampleService.java
+++ b/src/main/java/com/JayPi4c/RobbiSimulator/controller/examples/ExampleService.java
@@ -92,9 +92,10 @@ public static Optional> getAllTags() {
List tags;
try (Session session = HibernateUtils.getSessionFactory().openSession()) {
List allExamples = session.createQuery("from Example", Example.class).list();
+ logger.debug("Found {} examples", allExamples.size());
tags = allExamples.stream().flatMap(ex -> ex.getTags().stream()).distinct().toList();
}
- return Optional.ofNullable(tags);
+ return Optional.of(tags);
}
}
diff --git a/src/main/java/com/JayPi4c/RobbiSimulator/model/Territory.java b/src/main/java/com/JayPi4c/RobbiSimulator/model/Territory.java
index 3e2985c..01b9494 100644
--- a/src/main/java/com/JayPi4c/RobbiSimulator/model/Territory.java
+++ b/src/main/java/com/JayPi4c/RobbiSimulator/model/Territory.java
@@ -22,735 +22,735 @@
import lombok.extern.slf4j.Slf4j;
/**
- *
* This class contains all datastructures and utility functions to control the
* territory.
- *
+ *
* @author Jonas Pohl
- *
*/
@Slf4j
public class Territory extends Observable implements Serializable {
- private static final long serialVersionUID = 1L;
-
- private transient Robbi robbi;
-
- /**
- * Array-attribute to store the real territory
- */
- private Tile[][] tiles;
- /**
- * Attribute to save if the territory has changed.
- */
- private boolean sizeChanged = false;
-
- private static final int DEFAULT_NUMBER_OF_COLUMNS = 6;
- private static final int DEFAULT_NUMBER_OF_ROWS = 6;
-
- /**
- * Attribute to store the current number of columns of the territory.
- */
- private int numberOfColumns = DEFAULT_NUMBER_OF_COLUMNS;
- /**
- * Attribute to store the current number of rows of the territory.
- */
- private int numberOfRows = DEFAULT_NUMBER_OF_ROWS;
-
- /**
- * Creates a new Territory with a new robbi instance and initializes all tiles
- * to the default size.
- */
- public Territory() {
- robbi = new Robbi(this);
- tiles = new Tile[DEFAULT_NUMBER_OF_COLUMNS][DEFAULT_NUMBER_OF_ROWS];
- for (int i = 0; i < tiles.length; i++) {
- for (int j = 0; j < tiles[i].length; j++) {
- tiles[i][j] = new Tile();
- }
- }
- }
- // ============ HELPER =========
-
- /**
- * Saves the Territory in a TerritoryState in order to allow to restore it
- * later.
- *
- * @return a Memento Object of the territory
- */
- public synchronized TerritoryState save() {
- return new TerritoryState(numberOfColumns, numberOfRows, tiles, robbi);
- }
-
- /**
- * Restores the territory from a memento object.
- *
- * @param state the Memento object of the state the territory should restore to.
- */
- public void restore(TerritoryState state) {
- synchronized (this) {
- numberOfColumns = state.getNumberOfColumns();
- numberOfRows = state.getNumberOfRows();
- RobbiState robbiState = state.getRobbiState();
- this.robbi.setPosition(robbiState.getX(), robbiState.getY());
- this.robbi.setItem(robbiState.getItem());
- this.robbi.setFacing(robbiState.getFacing());
- this.tiles = state.getTiles();
- }
- setChanged();
- notifyAllObservers();
- }
-
- /**
- * Calculates the tile for the given x and y coordinates.
- *
- * @param x tiles x ordinate
- * @param y tiles y ordinate
- * @return the tile for the corresponding coordinate
- */
- public synchronized Tile getTile(int x, int y) {
- x += numberOfColumns;
- x %= numberOfColumns;
- y += numberOfRows;
- y %= numberOfRows;
- return tiles[x][y];
- }
-
- /**
- * Getter for the current number of rows.
- *
- * @return the current number of rows
- */
- public synchronized int getNumRows() {
- return numberOfRows;
- }
-
- /**
- * Getter for the current number of columns.
- *
- * @return the current number of columns
- */
- public synchronized int getNumCols() {
- return numberOfColumns;
- }
-
- /**
- * Getter to check if the size of the territory has changed.
- *
- * @return true if the size has changed, false otherwise
- */
- public synchronized boolean hasSizeChanged() {
- return sizeChanged;
- }
-
- /**
- * Setter to update if the size has changed.
- *
- * @param flag new value for the sizeChanged attribute
- */
- public synchronized void setSizeChanged(boolean flag) {
- this.sizeChanged = flag;
- }
-
- /**
- * Setter to update Robbi in the territory.
- *
- * @param robbi the new Robbi for the territory
- */
- public synchronized void setRobbi(Robbi robbi) {
- robbi.setTerritory(this);
- if (this.robbi != null) {
- robbi.setPosition(this.robbi.getX(), this.robbi.getY());
- robbi.setFacing(this.robbi.getFacing());
- robbi.setItem(this.robbi.getItem());
- }
- this.robbi = robbi;
- }
-
- /**
- * Getter for the current robbi in the territory.
- *
- * @return the current robbi
- */
- public synchronized Robbi getRobbi() {
- return robbi;
- }
-
- /**
- * Getter for robbis current direction.
- *
- * @return robbis current direction
- */
- public synchronized DIRECTION getRobbiDirection() {
- return robbi.getFacing();
- }
-
- /**
- * Getter for robbis current item.
- *
- * @return the item robbi is currently holding. null, if robbi does not have
- * one.
- */
- public synchronized Item getRobbiItem() {
- return robbi.getItem();
- }
-
- /**
- * Helper to bound a value to a given bound
- *
- * @param i the value to bound
- * @param bound the bound to limit i to
- * @return the normalized value of i
- */
- private int normalizeCoord(int i, int bound) {
- i %= bound;
- i += bound;
- i %= bound;
- return i;
- }
-
- /**
- * Checks if robbi is on the tile with the given coords.
- *
- * @param col the columns to check
- * @param row the row to check
- * @return true if and only if robbi is on the tile provided by col and row,
- * false otherwise
- */
- public synchronized boolean robbiOnTile(int col, int row) {
- return robbi.getX() == col && robbi.getY() == row;
- }
-
- /**
- * Placing the given item in the territory at the given x-y-position.
- *
- * @param item the item to place in the territory
- * @param x x-ordinate in the territory
- * @param y y-ordinate in the territory
- * @return true if the item has been placed in the territory, false otherwise
- */
- public boolean placeItem(Item item, int x, int y) {
- synchronized (this) {
- Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)];
- if (t.getItem() != null && !(t instanceof Stockpile))
- return false;
- t.setItem(item);
- }
- setChanged();
- notifyAllObservers();
- return true;
- }
-
- /**
- * Getter for the item in the territory at the given position.
- *
- * @param x x-ordinate in the territory
- * @param y y-ordinate in the territory
- * @return the item, which is placed at the given position
- */
- public synchronized Item getItem(int x, int y) {
- Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)];
- return t.getItem();
- }
-
- /**
- * Removes the item from the given position.
- *
- * @param x x-ordinate in the territory
- * @param y y-ordinate in the territory
- * @return the item that has been removed from the tile
- */
- public Item removeItem(int x, int y) {
- Item i = null;
- synchronized (this) {
- Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)];
- i = t.pickItem();
- }
- setChanged();
- notifyAllObservers();
- return i;
- }
-
- /**
- * Updates the territory to the given territory and updates robbi with the
- * additional information.
- * This method is used to update the territory to a territory state loaded from
- * a file.
- *
- * @param territory the new territory
- * @param item the item robbi has in his bag
- * @param x robbis x position
- * @param y robbis y positions
- * @param facing robbis facing
- * @throws InvalidTerritoryException if it is impossible to create the territory
- * with the given information
- *
- */
- public void update(Territory territory, Item item, int x, int y, DIRECTION facing)
- throws InvalidTerritoryException {
- for (int i = 0; i < territory.numberOfColumns; i++) {
- if (territory.tiles[i].length != territory.numberOfRows)
- throw new InvalidTerritoryException();
- }
- synchronized (this) {
- this.numberOfColumns = territory.numberOfColumns;
- this.numberOfRows = territory.numberOfRows;
- this.tiles = territory.tiles;
- this.sizeChanged = true;
- try {
- this.robbi.setPosition(x, y);
- } catch (Exception e) {
- throw new InvalidTerritoryException();
- }
- this.robbi.setFacing(facing);
- this.robbi.setItem(item);
- }
- setChanged();
- notifyAllObservers();
- }
-
- // ========= GUI FUNCTIONS ===========
-
- /**
- * Updates the size of the territory to the new size.
- *
- * @param newCols new number of columns in the territory
- * @param newRows new number of rows in the territory
- */
- public void changeSize(int newCols, int newRows) {
- synchronized (this) {
- if (newCols <= 0 || newRows <= 0)
- throw new IllegalArgumentException("Diese Größe ist für das Territorium nicht zulässig");
- if (newCols != numberOfColumns || newRows != numberOfRows)
- sizeChanged = true;
- else
- return;
- numberOfColumns = newCols;
- numberOfRows = newRows;
- // create the new territory
- Tile[][] newTiles = new Tile[numberOfColumns][numberOfRows];
- for (int i = 0; i < numberOfColumns; i++) {
- for (int j = 0; j < numberOfRows; j++) {
- newTiles[i][j] = new Tile();
- }
- }
-
- // copy the old territory into the new
- for (int i = 0; i < tiles.length && i < newCols; i++) {
- for (int j = 0; j < tiles[i].length && j < newRows; j++) {
- newTiles[i][j] = tiles[i][j];
- }
- }
-
- if (robbi.getX() >= numberOfColumns || robbi.getY() > numberOfRows) {
- if (newTiles[0][0] instanceof Hollow)
- newTiles[0][0] = new Tile();
- robbi.setPosition(0, 0);
- }
- tiles = newTiles;
-
- logger.debug("updated size to {}x{}", numberOfColumns, numberOfRows);
- }
- setChanged();
- notifyAllObservers();
- }
-
- /**
- * move robbi to the given position.
- *
- * @param x robbis new x-position
- * @param y robbis new y-position
- */
- public void placeRobbi(int x, int y) {
- synchronized (this) {
- if ((x >= 0 && x < numberOfColumns && y >= 0 && y < numberOfRows) && !(tiles[x][y] instanceof Hollow)) {
- robbi.setPosition(x, y);
- }
- }
- setChanged();
- notifyAllObservers();
- }
-
- /**
- * Place a new Hollow at the given position if it is in bounds and robbi is not
- * on the tile.
- *
- * @param x Hollows x-Position
- * @param y Hollows y-Position
- */
- public void placeHollow(int x, int y) {
- x = normalizeCoord(x, numberOfColumns);
- y = normalizeCoord(y, numberOfRows);
- synchronized (this) {
- if ((x < numberOfColumns && y < numberOfRows) && !(x == robbi.getX() && y == robbi.getY()))
- tiles[x][y] = new Hollow();
- }
- if ((x < numberOfColumns && y < numberOfRows) && !(x == robbi.getX() && y == robbi.getY())) {
- setChanged();
- notifyAllObservers();
- }
- }
-
- /**
- * Place a new PileOfScrap at the given position if it is in bounds.
- *
- * @param x PileOfScrap x-Position
- * @param y PileOfScrap y-Position
- */
- public void placePileOfScrap(int x, int y) {
- x = normalizeCoord(x, numberOfColumns);
- y = normalizeCoord(y, numberOfRows);
- synchronized (this) {
- tiles[x][y] = new PileOfScrap();
- }
- setChanged();
- notifyAllObservers();
-
- }
-
- /**
- * Place a new Stockpile at the given position if it is in bounds.
- *
- * @param x Stockpile x-Position
- * @param y Stockpile y-Position
- */
- public void placeStockpile(int x, int y) {
- x = normalizeCoord(x, numberOfColumns);
- y = normalizeCoord(y, numberOfRows);
- synchronized (this) {
- tiles[x][y] = new Stockpile();
- }
- setChanged();
- notifyAllObservers();
- }
-
- /**
- * Place a new Accu on the given tile if it is in bounds.
- *
- * @param x Accu x-Position
- * @param y Accu y-Position
- */
- public void placeAccu(int x, int y) {
- x = normalizeCoord(x, numberOfColumns);
- y = normalizeCoord(y, numberOfRows);
- synchronized (this) {
- tiles[x][y].setItem(new Accu());
- }
- setChanged();
- notifyAllObservers();
-
- }
-
- /**
- * Place a new Screw on the given tile if it is in bounds.
- *
- * @param x Screw x-Position
- * @param y Screw y-Position
- */
- public void placeScrew(int x, int y) {
- x = normalizeCoord(x, numberOfColumns);
- y = normalizeCoord(y, numberOfRows);
- synchronized (this) {
- tiles[x][y].setItem(new Screw());
- }
- setChanged();
- notifyAllObservers();
- }
-
- /**
- * Place a new Nut on the given tile if it is in bounds.
- *
- * @param x Nut x-Position
- * @param y Nut y-Position
- */
- public void placeNut(int x, int y) {
- x = normalizeCoord(x, numberOfColumns);
- y = normalizeCoord(y, numberOfRows);
- synchronized (this) {
- tiles[x][y].setItem(new Nut());
- }
- setChanged();
- notifyAllObservers();
- }
-
- /**
- * Removes all elements from the tile and replaces it with a new default tile.
- *
- * @param x Tile x-Position
- * @param y Tile y-Position
- */
- public void clearTile(int x, int y) {
- x = normalizeCoord(x, numberOfColumns);
- y = normalizeCoord(y, numberOfRows);
- synchronized (this) {
- tiles[x][y] = new Tile();
- }
- setChanged();
- notifyAllObservers();
- }
-
- /**
- * Updates this territory to the territory encoded as the XML-InputStream.
- * It uses the StAX Cursor API, since it is more efficient by not creating any
- * new objects.
- *
- * @param stream InputStream of the XML-encoded territory
- * @return true if the territory was build successfully, false otherwise
- */
- public boolean fromXML(InputStream stream) {
- try {
- Territory territory = new Territory();
- int robbiX = 0;
- int robbiY = 0;
- Item robbiItem = null;
- DIRECTION robbiDirection = DIRECTION.EAST;
- int x = 0;
- int y = 0;
- XMLInputFactory factory = XMLInputFactory.newInstance();
- factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
- factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
- XMLStreamReader parser = factory.createXMLStreamReader(stream);
- while (parser.hasNext()) {
- switch (parser.getEventType()) {
- case XMLStreamConstants.START_DOCUMENT:
- break;
- case XMLStreamConstants.END_DOCUMENT:
- parser.close();
- break;
- case XMLStreamConstants.START_ELEMENT:
- switch (parser.getLocalName()) {
- case "territory":
- int cols = Integer.parseInt(parser.getAttributeValue(null, "col"));
- int rows = Integer.parseInt(parser.getAttributeValue(null, "row"));
- territory.changeSize(cols, rows);
- break;
- case "pileofscrap":
- x = Integer.parseInt(parser.getAttributeValue(null, "col"));
- y = Integer.parseInt(parser.getAttributeValue(null, "row"));
- territory.tiles[x][y] = new PileOfScrap();
- break;
- case "hollow":
- x = Integer.parseInt(parser.getAttributeValue(null, "col"));
- y = Integer.parseInt(parser.getAttributeValue(null, "row"));
- territory.tiles[x][y] = new Hollow();
- break;
- case "stockpile":
- x = Integer.parseInt(parser.getAttributeValue(null, "col"));
- y = Integer.parseInt(parser.getAttributeValue(null, "row"));
- territory.tiles[x][y] = new Stockpile();
- break;
- case "tile":
- x = Integer.parseInt(parser.getAttributeValue(null, "col"));
- y = Integer.parseInt(parser.getAttributeValue(null, "row"));
- break;
- case "item":
- Item item = getItemFromParser(parser);
- if (x < 0 || y < 0) {
- robbiItem = item;
- } else {
- territory.placeItem(item, x, y);
- }
- break;
- case "facing":
- DIRECTION facing = DIRECTION.valueOf(parser.getAttributeValue(null, "facing"));
- robbiDirection = facing;
- break;
- case "robbi":
- robbiX = Integer.parseInt(parser.getAttributeValue(null, "col"));
- robbiY = Integer.parseInt(parser.getAttributeValue(null, "row"));
- x = -1;
- y = -1;
- break;
- default:
- break;
- }
- break;
- case XMLStreamConstants.CHARACTERS:
- break;
- case XMLStreamConstants.END_ELEMENT:
- break;
- default:
- break;
- }
- parser.next();
- }
- update(territory, robbiItem, robbiX, robbiY, robbiDirection);
- return true;
- } catch (Exception e) {
- return false;
- } finally {
- try {
- stream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * Helper to get the correct item from the parser.
- * Only use this method if you know for sure, that the parsers EventType is
- * Item.
- *
- * @param parser the parser to read the type from
- * @return the correct item class for this element
- */
- private Item getItemFromParser(XMLStreamReader parser) {
- String type = parser.getAttributeValue(null, "type");
- if (type.equals("Nut")) {
- return new Nut();
- } else if (type.equals("Accu")) {
- return new Accu();
- } else if (type.equals("Screw")) {
- return new Screw();
- }
- return null;
- }
-
- /**
- * Creates a ByteArrayOutputStream that contains the territory encoded as XML.
- *
- * @return ByteArrayOutputStream with XML-encoded territory.
- */
- public ByteArrayOutputStream toXML() {
- // load the dtd from resources
- String dtd;
- Optional dtdOpt = getDTD();
- if (dtdOpt.isPresent()) {
- dtd = dtdOpt.get();
- } else {
- logger.warn("Could not load dtd");
- return null;
- }
- try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- XMLOutputFactory factory = XMLOutputFactory.newInstance();
- XMLStreamWriter writer = factory.createXMLStreamWriter(baos, "utf-8");
-
- writer.writeStartDocument("utf-8", "1.0");
- writer.writeCharacters("\n");
-
- writer.writeDTD("");
- writer.writeCharacters("\n");
- synchronized (this) {
- writer.writeStartElement("territory");
- writer.writeAttribute("col", Integer.toString(getNumCols()));
- writer.writeAttribute("row", Integer.toString(getNumRows()));
- writer.writeCharacters("\n");
- for (int i = 0; i < getNumCols(); i++) {
- for (int j = 0; j < getNumRows(); j++) {
- Tile t = getTile(i, j);
- if (t instanceof Hollow) {
- writer.writeStartElement("hollow");
- writer.writeAttribute("col", Integer.toString(i));
- writer.writeAttribute("row", Integer.toString(j));
- writer.writeCharacters("\n");
- } else if (t instanceof PileOfScrap) {
- writer.writeStartElement("pileofscrap");
- writer.writeAttribute("col", Integer.toString(i));
- writer.writeAttribute("row", Integer.toString(j));
- writer.writeCharacters("\n");
- } else if (t instanceof Stockpile stockpile) {
- writer.writeStartElement("stockpile");
- writer.writeAttribute("col", Integer.toString(i));
- writer.writeAttribute("row", Integer.toString(j));
- writer.writeCharacters("\n");
- for (Item item : stockpile.getAllItems()) {
- writeItem(writer, item);
- }
- } else {
- writer.writeStartElement("tile");
- writer.writeAttribute("col", Integer.toString(i));
- writer.writeAttribute("row", Integer.toString(j));
- writer.writeCharacters("\n");
- if (t.getItem() != null) {
- writeItem(writer, t.getItem());
- }
- }
- writer.writeEndElement();
- writer.writeCharacters("\n");
- }
- }
- writer.writeStartElement("robbi");
- writer.writeAttribute("col", Integer.toString(getRobbiX()));
- writer.writeAttribute("row", Integer.toString(getRobbiY()));
- writer.writeCharacters("\n");
- writer.writeStartElement("facing");
- writer.writeAttribute("facing", getRobbiDirection().toString());
- writer.writeEndElement();
- writer.writeCharacters("\n");
- if (getRobbiItem() != null) {
- writeItem(writer, getRobbiItem());
- }
- }
- writer.writeEndElement();
- writer.writeCharacters("\n");
- writer.writeEndElement();
- writer.writeCharacters("\n");
- writer.writeEndDocument();
- writer.close();
-
- return baos;
- } catch (FactoryConfigurationError | XMLStreamException e) {
- e.printStackTrace();
- logger.error("failed to save as XML.");
- return null;
- }
- }
-
- /**
- * Helper to store an item in an xml-file.
- *
- * @param writer the writer, the item needs to be written to
- * @param item the item to write
- * @throws XMLStreamException if the item cannot be written
- */
- private void writeItem(XMLStreamWriter writer, Item item) throws XMLStreamException {
- writer.writeStartElement("item");
- writer.writeAttribute("type", item.getClass().getSimpleName());
- writer.writeEndElement();
- writer.writeCharacters("\n");
- }
-
- /**
- * Loads the dtd needed to save the territory as a xml-File from the resources
- * folder
- *
- * @return the dtd String defined in resources/xml/simulator.dtd
- */
- private Optional getDTD() {
- try (BufferedReader reader = new BufferedReader(
- new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("xml/simulator.dtd")))) {
-
- StringBuilder builder = new StringBuilder();
- String s;
- while ((s = reader.readLine()) != null) {
- builder.append(s);
- builder.append(System.lineSeparator());
- }
- return Optional.of(builder.toString());
- } catch (IOException e) {
- e.printStackTrace();
- }
- return Optional.empty();
- }
-
- /**
- * Getter for Robbis x Position.
- *
- * @return robbis x Position
- */
- public synchronized int getRobbiX() {
- return robbi.getX();
- }
-
- /**
- * Getter for Robbis y Position.
- *
- * @return robbis y Position
- */
- public synchronized int getRobbiY() {
- return robbi.getY();
- }
+ private static final long serialVersionUID = 1L;
+
+ private transient Robbi robbi;
+
+ /**
+ * Array-attribute to store the real territory
+ */
+ private Tile[][] tiles;
+ /**
+ * Attribute to save if the territory has changed.
+ */
+ private boolean sizeChanged = false;
+
+ private static final int DEFAULT_NUMBER_OF_COLUMNS = 6;
+ private static final int DEFAULT_NUMBER_OF_ROWS = 6;
+
+ /**
+ * Attribute to store the current number of columns of the territory.
+ */
+ private int numberOfColumns = DEFAULT_NUMBER_OF_COLUMNS;
+ /**
+ * Attribute to store the current number of rows of the territory.
+ */
+ private int numberOfRows = DEFAULT_NUMBER_OF_ROWS;
+
+ /**
+ * Creates a new Territory with a new robbi instance and initializes all tiles
+ * to the default size.
+ */
+ public Territory() {
+ robbi = new Robbi(this);
+ tiles = new Tile[DEFAULT_NUMBER_OF_COLUMNS][DEFAULT_NUMBER_OF_ROWS];
+ for (int i = 0; i < tiles.length; i++) {
+ for (int j = 0; j < tiles[i].length; j++) {
+ tiles[i][j] = new Tile();
+ }
+ }
+ }
+ // ============ HELPER =========
+
+ /**
+ * Saves the Territory in a TerritoryState in order to allow to restore it
+ * later.
+ *
+ * @return a Memento Object of the territory
+ */
+ public synchronized TerritoryState save() {
+ return new TerritoryState(numberOfColumns, numberOfRows, tiles, robbi);
+ }
+
+ /**
+ * Restores the territory from a memento object.
+ *
+ * @param state the Memento object of the state the territory should restore to.
+ */
+ public void restore(TerritoryState state) {
+ synchronized (this) {
+ numberOfColumns = state.getNumberOfColumns();
+ numberOfRows = state.getNumberOfRows();
+ RobbiState robbiState = state.getRobbiState();
+ this.robbi.setPosition(robbiState.getX(), robbiState.getY());
+ this.robbi.setItem(robbiState.getItem());
+ this.robbi.setFacing(robbiState.getFacing());
+ this.tiles = state.getTiles();
+ }
+ setChanged();
+ notifyAllObservers();
+ }
+
+ /**
+ * Calculates the tile for the given x and y coordinates.
+ *
+ * @param x tiles x ordinate
+ * @param y tiles y ordinate
+ * @return the tile for the corresponding coordinate
+ */
+ public synchronized Tile getTile(int x, int y) {
+ x += numberOfColumns;
+ x %= numberOfColumns;
+ y += numberOfRows;
+ y %= numberOfRows;
+ return tiles[x][y];
+ }
+
+ /**
+ * Getter for the current number of rows.
+ *
+ * @return the current number of rows
+ */
+ public synchronized int getNumRows() {
+ return numberOfRows;
+ }
+
+ /**
+ * Getter for the current number of columns.
+ *
+ * @return the current number of columns
+ */
+ public synchronized int getNumCols() {
+ return numberOfColumns;
+ }
+
+ /**
+ * Getter to check if the size of the territory has changed.
+ *
+ * @return true if the size has changed, false otherwise
+ */
+ public synchronized boolean hasSizeChanged() {
+ return sizeChanged;
+ }
+
+ /**
+ * Setter to update if the size has changed.
+ *
+ * @param flag new value for the sizeChanged attribute
+ */
+ public synchronized void setSizeChanged(boolean flag) {
+ this.sizeChanged = flag;
+ }
+
+ /**
+ * Setter to update Robbi in the territory.
+ *
+ * @param robbi the new Robbi for the territory
+ */
+ public synchronized void setRobbi(Robbi robbi) {
+ robbi.setTerritory(this);
+ if (this.robbi != null) {
+ robbi.setPosition(this.robbi.getX(), this.robbi.getY());
+ robbi.setFacing(this.robbi.getFacing());
+ robbi.setItem(this.robbi.getItem());
+ }
+ this.robbi = robbi;
+ }
+
+ /**
+ * Getter for the current robbi in the territory.
+ *
+ * @return the current robbi
+ */
+ public synchronized Robbi getRobbi() {
+ return robbi;
+ }
+
+ /**
+ * Getter for robbis current direction.
+ *
+ * @return robbis current direction
+ */
+ public synchronized DIRECTION getRobbiDirection() {
+ return robbi.getFacing();
+ }
+
+ /**
+ * Getter for robbis current item.
+ *
+ * @return the item robbi is currently holding. null, if robbi does not have
+ * one.
+ */
+ public synchronized Item getRobbiItem() {
+ return robbi.getItem();
+ }
+
+ /**
+ * Helper to bound a value to a given bound
+ *
+ * @param i the value to bound
+ * @param bound the bound to limit i to
+ * @return the normalized value of i
+ */
+ private int normalizeCoord(int i, int bound) {
+ i %= bound;
+ i += bound;
+ i %= bound;
+ return i;
+ }
+
+ /**
+ * Checks if robbi is on the tile with the given coords.
+ *
+ * @param col the columns to check
+ * @param row the row to check
+ * @return true if and only if robbi is on the tile provided by col and row,
+ * false otherwise
+ */
+ public synchronized boolean robbiOnTile(int col, int row) {
+ return robbi.getX() == col && robbi.getY() == row;
+ }
+
+ /**
+ * Placing the given item in the territory at the given x-y-position.
+ *
+ * @param item the item to place in the territory
+ * @param x x-ordinate in the territory
+ * @param y y-ordinate in the territory
+ * @return true if the item has been placed in the territory, false otherwise
+ */
+ public boolean placeItem(Item item, int x, int y) {
+ synchronized (this) {
+ Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)];
+ if (t.getItem() != null && !(t instanceof Stockpile))
+ return false;
+ t.setItem(item);
+ }
+ setChanged();
+ notifyAllObservers();
+ return true;
+ }
+
+ /**
+ * Getter for the item in the territory at the given position.
+ *
+ * @param x x-ordinate in the territory
+ * @param y y-ordinate in the territory
+ * @return the item, which is placed at the given position
+ */
+ public synchronized Item getItem(int x, int y) {
+ Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)];
+ return t.getItem();
+ }
+
+ /**
+ * Removes the item from the given position.
+ *
+ * @param x x-ordinate in the territory
+ * @param y y-ordinate in the territory
+ * @return the item that has been removed from the tile
+ */
+ public Item removeItem(int x, int y) {
+ Item i = null;
+ synchronized (this) {
+ Tile t = tiles[normalizeCoord(x, numberOfColumns)][normalizeCoord(y, numberOfRows)];
+ i = t.pickItem();
+ }
+ setChanged();
+ notifyAllObservers();
+ return i;
+ }
+
+ /**
+ * Updates the territory to the given territory and updates robbi with the
+ * additional information.
+ * This method is used to update the territory to a territory state loaded from
+ * a file.
+ *
+ * @param territory the new territory
+ * @param item the item robbi has in his bag
+ * @param x robbis x position
+ * @param y robbis y positions
+ * @param facing robbis facing
+ * @throws InvalidTerritoryException if it is impossible to create the territory
+ * with the given information
+ */
+ public void update(Territory territory, Item item, int x, int y, DIRECTION facing)
+ throws InvalidTerritoryException {
+ for (int i = 0; i < territory.numberOfColumns; i++) {
+ if (territory.tiles[i].length != territory.numberOfRows)
+ throw new InvalidTerritoryException();
+ }
+ synchronized (this) {
+ this.numberOfColumns = territory.numberOfColumns;
+ this.numberOfRows = territory.numberOfRows;
+ this.tiles = territory.tiles;
+ this.sizeChanged = true;
+ try {
+ this.robbi.setPosition(x, y);
+ } catch (Exception e) {
+ throw new InvalidTerritoryException();
+ }
+ this.robbi.setFacing(facing);
+ this.robbi.setItem(item);
+ }
+ setChanged();
+ notifyAllObservers();
+ }
+
+ // ========= GUI FUNCTIONS ===========
+
+ /**
+ * Updates the size of the territory to the new size.
+ *
+ * @param newCols new number of columns in the territory
+ * @param newRows new number of rows in the territory
+ */
+ public void changeSize(int newCols, int newRows) {
+ synchronized (this) {
+ if (newCols <= 0 || newRows <= 0)
+ throw new IllegalArgumentException("Diese Größe ist für das Territorium nicht zulässig");
+ if (newCols != numberOfColumns || newRows != numberOfRows)
+ sizeChanged = true;
+ else
+ return;
+ numberOfColumns = newCols;
+ numberOfRows = newRows;
+ // create the new territory
+ Tile[][] newTiles = new Tile[numberOfColumns][numberOfRows];
+ for (int i = 0; i < numberOfColumns; i++) {
+ for (int j = 0; j < numberOfRows; j++) {
+ newTiles[i][j] = new Tile();
+ }
+ }
+
+ // copy the old territory into the new
+ for (int i = 0; i < tiles.length && i < newCols; i++) {
+ for (int j = 0; j < tiles[i].length && j < newRows; j++) {
+ newTiles[i][j] = tiles[i][j];
+ }
+ }
+
+ if (robbi.getX() >= numberOfColumns || robbi.getY() > numberOfRows) {
+ if (newTiles[0][0] instanceof Hollow)
+ newTiles[0][0] = new Tile();
+ robbi.setPosition(0, 0);
+ }
+ tiles = newTiles;
+
+ logger.debug("updated size to {}x{}", numberOfColumns, numberOfRows);
+ }
+ setChanged();
+ notifyAllObservers();
+ }
+
+ /**
+ * move robbi to the given position.
+ *
+ * @param x robbis new x-position
+ * @param y robbis new y-position
+ */
+ public void placeRobbi(int x, int y) {
+ synchronized (this) {
+ if ((x >= 0 && x < numberOfColumns && y >= 0 && y < numberOfRows) && !(tiles[x][y] instanceof Hollow)) {
+ robbi.setPosition(x, y);
+ }
+ }
+ setChanged();
+ notifyAllObservers();
+ }
+
+ /**
+ * Place a new Hollow at the given position if it is in bounds and robbi is not
+ * on the tile.
+ *
+ * @param x Hollows x-Position
+ * @param y Hollows y-Position
+ */
+ public void placeHollow(int x, int y) {
+ x = normalizeCoord(x, numberOfColumns);
+ y = normalizeCoord(y, numberOfRows);
+ synchronized (this) {
+ if ((x < numberOfColumns && y < numberOfRows) && !(x == robbi.getX() && y == robbi.getY()))
+ tiles[x][y] = new Hollow();
+ }
+ if ((x < numberOfColumns && y < numberOfRows) && !(x == robbi.getX() && y == robbi.getY())) {
+ setChanged();
+ notifyAllObservers();
+ }
+ }
+
+ /**
+ * Place a new PileOfScrap at the given position if it is in bounds.
+ *
+ * @param x PileOfScrap x-Position
+ * @param y PileOfScrap y-Position
+ */
+ public void placePileOfScrap(int x, int y) {
+ x = normalizeCoord(x, numberOfColumns);
+ y = normalizeCoord(y, numberOfRows);
+ synchronized (this) {
+ tiles[x][y] = new PileOfScrap();
+ }
+ setChanged();
+ notifyAllObservers();
+
+ }
+
+ /**
+ * Place a new Stockpile at the given position if it is in bounds.
+ *
+ * @param x Stockpile x-Position
+ * @param y Stockpile y-Position
+ */
+ public void placeStockpile(int x, int y) {
+ x = normalizeCoord(x, numberOfColumns);
+ y = normalizeCoord(y, numberOfRows);
+ synchronized (this) {
+ tiles[x][y] = new Stockpile();
+ }
+ setChanged();
+ notifyAllObservers();
+ }
+
+ /**
+ * Place a new Accu on the given tile if it is in bounds.
+ *
+ * @param x Accu x-Position
+ * @param y Accu y-Position
+ */
+ public void placeAccu(int x, int y) {
+ x = normalizeCoord(x, numberOfColumns);
+ y = normalizeCoord(y, numberOfRows);
+ synchronized (this) {
+ tiles[x][y].setItem(new Accu());
+ }
+ setChanged();
+ notifyAllObservers();
+
+ }
+
+ /**
+ * Place a new Screw on the given tile if it is in bounds.
+ *
+ * @param x Screw x-Position
+ * @param y Screw y-Position
+ */
+ public void placeScrew(int x, int y) {
+ x = normalizeCoord(x, numberOfColumns);
+ y = normalizeCoord(y, numberOfRows);
+ synchronized (this) {
+ tiles[x][y].setItem(new Screw());
+ }
+ setChanged();
+ notifyAllObservers();
+ }
+
+ /**
+ * Place a new Nut on the given tile if it is in bounds.
+ *
+ * @param x Nut x-Position
+ * @param y Nut y-Position
+ */
+ public void placeNut(int x, int y) {
+ x = normalizeCoord(x, numberOfColumns);
+ y = normalizeCoord(y, numberOfRows);
+ synchronized (this) {
+ tiles[x][y].setItem(new Nut());
+ }
+ setChanged();
+ notifyAllObservers();
+ }
+
+ /**
+ * Removes all elements from the tile and replaces it with a new default tile.
+ *
+ * @param x Tile x-Position
+ * @param y Tile y-Position
+ */
+ public void clearTile(int x, int y) {
+ x = normalizeCoord(x, numberOfColumns);
+ y = normalizeCoord(y, numberOfRows);
+ synchronized (this) {
+ tiles[x][y] = new Tile();
+ }
+ setChanged();
+ notifyAllObservers();
+ }
+
+ /**
+ * Updates this territory to the territory encoded as the XML-InputStream.
+ * It uses the StAX Cursor API, since it is more efficient by not creating any
+ * new objects.
+ *
+ * @param stream InputStream of the XML-encoded territory
+ * @return true if the territory was build successfully, false otherwise
+ */
+ public boolean fromXML(InputStream stream) {
+ try {
+ Territory territory = new Territory();
+ int robbiX = 0;
+ int robbiY = 0;
+ Item robbiItem = null;
+ DIRECTION robbiDirection = DIRECTION.EAST;
+ int x = 0;
+ int y = 0;
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ XMLStreamReader parser = factory.createXMLStreamReader(stream);
+ while (parser.hasNext()) {
+ switch (parser.getEventType()) {
+ case XMLStreamConstants.START_DOCUMENT:
+ break;
+ case XMLStreamConstants.END_DOCUMENT:
+ parser.close();
+ break;
+ case XMLStreamConstants.START_ELEMENT:
+ switch (parser.getLocalName()) {
+ case "territory":
+ int cols = Integer.parseInt(parser.getAttributeValue(null, "col"));
+ int rows = Integer.parseInt(parser.getAttributeValue(null, "row"));
+ territory.changeSize(cols, rows);
+ break;
+ case "pileofscrap":
+ x = Integer.parseInt(parser.getAttributeValue(null, "col"));
+ y = Integer.parseInt(parser.getAttributeValue(null, "row"));
+ territory.tiles[x][y] = new PileOfScrap();
+ break;
+ case "hollow":
+ x = Integer.parseInt(parser.getAttributeValue(null, "col"));
+ y = Integer.parseInt(parser.getAttributeValue(null, "row"));
+ territory.tiles[x][y] = new Hollow();
+ break;
+ case "stockpile":
+ x = Integer.parseInt(parser.getAttributeValue(null, "col"));
+ y = Integer.parseInt(parser.getAttributeValue(null, "row"));
+ territory.tiles[x][y] = new Stockpile();
+ break;
+ case "tile":
+ x = Integer.parseInt(parser.getAttributeValue(null, "col"));
+ y = Integer.parseInt(parser.getAttributeValue(null, "row"));
+ break;
+ case "item":
+ Item item = getItemFromParser(parser);
+ if (x < 0 || y < 0) {
+ robbiItem = item;
+ } else {
+ territory.placeItem(item, x, y);
+ }
+ break;
+ case "facing":
+ DIRECTION facing = DIRECTION.valueOf(parser.getAttributeValue(null, "facing"));
+ robbiDirection = facing;
+ break;
+ case "robbi":
+ robbiX = Integer.parseInt(parser.getAttributeValue(null, "col"));
+ robbiY = Integer.parseInt(parser.getAttributeValue(null, "row"));
+ x = -1;
+ y = -1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case XMLStreamConstants.CHARACTERS:
+ break;
+ case XMLStreamConstants.END_ELEMENT:
+ break;
+ default:
+ break;
+ }
+ parser.next();
+ }
+ update(territory, robbiItem, robbiX, robbiY, robbiDirection);
+ return true;
+ } catch (Exception e) {
+ return false;
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Helper to get the correct item from the parser.
+ * Only use this method if you know for sure, that the parsers EventType is
+ * Item.
+ *
+ * @param parser the parser to read the type from
+ * @return the correct item class for this element
+ */
+ private Item getItemFromParser(XMLStreamReader parser) {
+ String type = parser.getAttributeValue(null, "type");
+ if (type.equals("Nut")) {
+ return new Nut();
+ } else if (type.equals("Accu")) {
+ return new Accu();
+ } else if (type.equals("Screw")) {
+ return new Screw();
+ }
+ return null;
+ }
+
+ /**
+ * Creates a ByteArrayOutputStream that contains the territory encoded as XML.
+ *
+ * @return ByteArrayOutputStream with XML-encoded territory.
+ */
+ public ByteArrayOutputStream toXML() {
+ // load the dtd from resources
+ String dtd;
+ Optional dtdOpt = getDTD();
+ if (dtdOpt.isPresent()) {
+ dtd = dtdOpt.get();
+ } else {
+ logger.warn("Could not load dtd");
+ return null;
+ }
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ XMLOutputFactory factory = XMLOutputFactory.newInstance();
+ XMLStreamWriter writer = factory.createXMLStreamWriter(baos, "utf-8");
+
+ writer.writeStartDocument("utf-8", "1.0");
+ writer.writeCharacters("\n");
+
+ writer.writeDTD("");
+ writer.writeCharacters("\n");
+ synchronized (this) {
+ writer.writeStartElement("territory");
+ writer.writeAttribute("col", Integer.toString(getNumCols()));
+ writer.writeAttribute("row", Integer.toString(getNumRows()));
+ writer.writeCharacters("\n");
+ for (int i = 0; i < getNumCols(); i++) {
+ for (int j = 0; j < getNumRows(); j++) {
+ Tile t = getTile(i, j);
+ if (t instanceof Hollow) {
+ writer.writeStartElement("hollow");
+ writer.writeAttribute("col", Integer.toString(i));
+ writer.writeAttribute("row", Integer.toString(j));
+ writer.writeCharacters("\n");
+ } else if (t instanceof PileOfScrap) {
+ writer.writeStartElement("pileofscrap");
+ writer.writeAttribute("col", Integer.toString(i));
+ writer.writeAttribute("row", Integer.toString(j));
+ writer.writeCharacters("\n");
+ } else if (t instanceof Stockpile stockpile) {
+ writer.writeStartElement("stockpile");
+ writer.writeAttribute("col", Integer.toString(i));
+ writer.writeAttribute("row", Integer.toString(j));
+ writer.writeCharacters("\n");
+ for (Item item : stockpile.getAllItems()) {
+ writeItem(writer, item);
+ }
+ } else {
+ writer.writeStartElement("tile");
+ writer.writeAttribute("col", Integer.toString(i));
+ writer.writeAttribute("row", Integer.toString(j));
+ writer.writeCharacters("\n");
+ if (t.getItem() != null) {
+ writeItem(writer, t.getItem());
+ }
+ }
+ writer.writeEndElement();
+ writer.writeCharacters("\n");
+ }
+ }
+ writer.writeStartElement("robbi");
+ writer.writeAttribute("col", Integer.toString(getRobbiX()));
+ writer.writeAttribute("row", Integer.toString(getRobbiY()));
+ writer.writeCharacters("\n");
+ writer.writeStartElement("facing");
+ writer.writeAttribute("facing", getRobbiDirection().toString());
+ writer.writeEndElement();
+ writer.writeCharacters("\n");
+ if (getRobbiItem() != null) {
+ writeItem(writer, getRobbiItem());
+ }
+ }
+ writer.writeEndElement();
+ writer.writeCharacters("\n");
+ writer.writeEndElement();
+ writer.writeCharacters("\n");
+ writer.writeEndDocument();
+ writer.close();
+
+ return baos;
+ } catch (FactoryConfigurationError | XMLStreamException e) {
+ e.printStackTrace();
+ logger.error("failed to save as XML.");
+ return null;
+ }
+ }
+
+ /**
+ * Helper to store an item in an xml-file.
+ *
+ * @param writer the writer, the item needs to be written to
+ * @param item the item to write
+ * @throws XMLStreamException if the item cannot be written
+ */
+ private void writeItem(XMLStreamWriter writer, Item item) throws XMLStreamException {
+ writer.writeStartElement("item");
+ writer.writeAttribute("type", item.getClass().getSimpleName());
+ writer.writeEndElement();
+ writer.writeCharacters("\n");
+ }
+
+ /**
+ * Loads the dtd needed to save the territory as a xml-File from the resources
+ * folder
+ *
+ * @return the dtd String defined in resources/xml/simulator.dtd
+ */
+ private Optional getDTD() {
+ Optional module = ModuleLayer.boot().findModule("RobbiSimulator");
+ if(module.isEmpty()) {
+ logger.warn("Could not load dtd");
+ return Optional.empty();
+ }
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(module.get().getResourceAsStream("/xml/simulator.dtd")))) {
+ StringBuilder builder = new StringBuilder();
+ String s;
+ while ((s = reader.readLine()) != null) {
+ builder.append(s);
+ builder.append(System.lineSeparator());
+ }
+ return Optional.of(builder.toString());
+ } catch (IOException e) {
+ logger.warn("Could not load dtd", e);
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Getter for Robbis x Position.
+ *
+ * @return robbis x Position
+ */
+ public synchronized int getRobbiX() {
+ return robbi.getX();
+ }
+
+ /**
+ * Getter for Robbis y Position.
+ *
+ * @return robbis y Position
+ */
+ public synchronized int getRobbiY() {
+ return robbi.getY();
+ }
}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 8d99a8a..087c4f8 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -21,6 +21,8 @@
requires org.apache.logging.log4j;
requires org.apache.derby.tools;
+ opens com.JayPi4c.RobbiSimulator.controller.examples to org.hibernate.orm.core;
+
exports com.JayPi4c.RobbiSimulator.utils.annotations;
exports com.JayPi4c.RobbiSimulator.model;
exports com.JayPi4c.RobbiSimulator;
diff --git a/src/main/resources/hibernate.cfg.xml b/src/main/resources/hibernate.cfg.xml
index 5c22075..8849777 100644
--- a/src/main/resources/hibernate.cfg.xml
+++ b/src/main/resources/hibernate.cfg.xml
@@ -16,7 +16,7 @@
thread
- org.hibernate.dialect.DerbyTenSevenDialect
-
+ org.hibernate.dialect.DerbyDialect
+
\ No newline at end of file
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
index 67e1900..49a51bc 100644
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -1,22 +1,25 @@
-
- %d{HH:mm:ss.SSS} [%t] %-5level %logger{36}(%F:%L) - %msg%n
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ %d{HH:mm:ss.SSS} [%t] %-5level %logger{36}(%F:%L) - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file