Skip to content
Snippets Groups Projects
Commit 9af70d02 authored by jez04's avatar jez04
Browse files

feat: lab05 assignment

parent f9fe3fa0
Branches master
No related merge requests found
Pipeline #2578 failed with stages
Showing
with 320 additions and 126 deletions
......@@ -3,10 +3,10 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cz.vsb.fei.java2</groupId>
<artifactId>java2-lab04-v3</artifactId>
<artifactId>java2-lab05-v3</artifactId>
<version>0.0.1-SNAPHOST</version>
<packaging>jar</packaging>
<name>java2-lab04-v3</name>
<name>java2-lab05-v3</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
......@@ -57,7 +57,6 @@
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.3.232</version>
<scope>runtime</scope>
</dependency>
<dependency>
......
......@@ -3,12 +3,17 @@ package lab;
import lab.storage.DbConnector;
import lab.storage.ScoreStorageInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Builder;
import lombok.Builder.Default;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Getter
@AllArgsConstructor
@lombok.Builder(toBuilder = true)
@EqualsAndHashCode
@ToString
@Builder(toBuilder = true)
public class Setup {
@Getter
......
package lab.data;
public enum Level {
EASY, MEDIUM, HARD;
}
......@@ -3,6 +3,7 @@ package lab.data;
import java.util.Random;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
......@@ -13,15 +14,19 @@ import lombok.ToString;
@AllArgsConstructor
@EqualsAndHashCode
@ToString
@Builder(toBuilder = true)
public class Score {
private static final Random RANDOM = new Random();
private Long id;
private String name;
private int points;
private Level level;
public static Score generate() {
return new Score(getRandomNick(), RANDOM.nextInt(50, 300));
return new Score(null, getRandomNick(), RANDOM.nextInt(50, 300), Level.values()[RANDOM.nextInt(Level.values().length)]);
}
public static final String[] nicks = { "CyberSurfer", "PixelPioneer", "SocialSavvy", "DigitalDynamo", "ByteBuddy", "InstaGuru",
......@@ -46,4 +51,4 @@ public class Score {
return nicks[RANDOM.nextInt(nicks.length)];
}
}
}
\ No newline at end of file
......@@ -6,6 +6,7 @@ import java.sql.SQLException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.h2.tools.Server;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
......@@ -14,6 +15,7 @@ import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import lab.Setup;
import lab.storage.JpaConnector;
/**
* Class <b>App</b> - extends class Application and it is an entry point of the
......@@ -30,10 +32,24 @@ public class App extends Application {
public static void main(String[] args) {
log.info("Application lauched");
Setup.configure(Setup.getInstanceForHardcoreGame().toBuilder().boatHitPulseX(-20).build());
Setup.configure(Setup.builder().scoreStorageInterface(new JpaConnector()).build());
startH2WebServerToInspectDb();
launch(args);
}
private static void startH2WebServerToInspectDb() {
//Start HTTP server for access H2 DB for look inside
try {
Server server = Server.createWebServer();
log.info(server.getURL());
server.start();
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void start(Stage primaryStage) {
try {
......@@ -84,6 +100,7 @@ public class App extends Application {
if(gameController != null) {
gameController.stop();
}
Setup.getInstance().getScoreStorageInterface().stop();
log.info("Exiting game");
System.exit(0);
}
......
package lab.gui;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -10,13 +8,8 @@ import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import lab.Setup;
import lab.data.Score;
import lab.game.DrawingThread;
import lab.game.LochNess;
import lab.game.Scene;
......@@ -27,24 +20,6 @@ public class GameController {
private Scene scene;
@FXML
private Button btnGenerateScore;
@FXML
private Button btnLoadAll;
@FXML
private Button btnLoadFirstTen;
@FXML
private TableView<Score> scores;
@FXML
private TableColumn<Score, String> nickColumn;
@FXML
private TableColumn<Score, Integer> pointsColumn;
@FXML
private Slider boatPosition;
......@@ -79,28 +54,6 @@ public class GameController {
playerName.setText(String.format("Number of dead lochnesses: %03d", deadLochNessCounter));
}
@FXML
void btnGenerateScoreAction(ActionEvent event) {
Score score = Score.generate();
this.scores.getItems().add(score);
Setup.getInstance().getScoreStorageInterface().insertScore(score);
}
@FXML
void btnLoadAllAction(ActionEvent event) {
updateScoreTable(Setup.getInstance().getScoreStorageInterface().getAll());
}
@FXML
void btnLoadFirstTenAction(ActionEvent event) {
updateScoreTable(Setup.getInstance().getScoreStorageInterface().getFirstTen());
}
private void updateScoreTable(List<Score> scores) {
this.scores.getItems().clear();
this.scores.getItems().addAll(scores);
}
@FXML
void initialize() {
assert boatPosition != null : "fx:id=\"angle\" was not injected: check your FXML file 'gameWindow.fxml'.";
......
package lab.gui;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
......@@ -35,6 +38,10 @@ public class MainScreenController {
@FXML
private Button btnLoadFirstTen;
@FXML
private Button btnDelete;
@FXML
private TableView<Score> scores;
......@@ -44,6 +51,9 @@ public class MainScreenController {
@FXML
private TableColumn<Score, Integer> pointsColumn;
@FXML
private TableColumn<Score, Integer> difficultColumn;
@FXML
private ToggleGroup difficult;
......@@ -75,7 +85,7 @@ public class MainScreenController {
void btnGenerateScoreAction(ActionEvent event) {
Score score = Score.generate();
this.scores.getItems().add(score);
Setup.getInstance().getScoreStorageInterface().insertScore(score);
Setup.getInstance().getScoreStorageInterface().save(score);
}
@FXML
......@@ -87,6 +97,13 @@ public class MainScreenController {
void btnLoadFirstTenAction(ActionEvent event) {
updateScoreTable(Setup.getInstance().getScoreStorageInterface().getFirstTen());
}
@FXML
void btnDeleteAction(ActionEvent event) {
List<Score> selectedScores = new ArrayList<>(scores.getSelectionModel().getSelectedItems());
Setup.getInstance().getScoreStorageInterface().delete(selectedScores);
updateScoreTable(Setup.getInstance().getScoreStorageInterface().getAll());
}
private void updateScoreTable(List<Score> scores) {
this.scores.getItems().clear();
......@@ -103,12 +120,19 @@ public class MainScreenController {
nickColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
pointsColumn.setCellValueFactory(new PropertyValueFactory<>("points"));
difficultColumn.setCellValueFactory(new PropertyValueFactory<>("level"));
btnDelete.setDisable(true);
scores.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
scores.getSelectionModel().getSelectedItems()
.addListener((ListChangeListener.Change<? extends Score> change) ->
btnDelete.setDisable(change.getList().isEmpty()));
initDB();
initStorage();
log.info("Screeen initialized.");
}
private void initDB() {
private void initStorage() {
Setup.getInstance().getScoreStorageInterface().init();
scores.getItems().addAll(Setup.getInstance().getScoreStorageInterface().getAll());
}
......
......@@ -9,16 +9,10 @@ import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import lab.data.Score;
public class DbConnector implements ScoreStorageInterface {
private static Logger log = LogManager.getLogger(DbConnector.class);
private static final String JDBC_CONECTIN_STRING = "jdbc:h2:file:./scoreDB";
@Override
......@@ -37,7 +31,7 @@ public class DbConnector implements ScoreStorageInterface {
Statement stm = con.createStatement();
ResultSet rs = stm.executeQuery(query);) {
while (rs.next()) {
result.add(new Score(rs.getString("nick"), rs.getInt("points")));
result.add(new Score(null, rs.getString("nick"), rs.getInt("points"), null));
}
} catch (SQLException e) {
e.printStackTrace();
......@@ -56,7 +50,7 @@ public class DbConnector implements ScoreStorageInterface {
}
@Override
public void insertScore(Score score) {
public Score save(Score score) {
try (Connection con = DriverManager.getConnection(JDBC_CONECTIN_STRING);
PreparedStatement stm = con.prepareStatement("INSERT INTO scores VALUES (?, ?)");) {
stm.setString(1, score.getName());
......@@ -65,5 +59,26 @@ public class DbConnector implements ScoreStorageInterface {
} catch (SQLException e) {
e.printStackTrace();
}
return score;
}
@Override
public void delete(List<Score> scores) {
try (Connection con = DriverManager.getConnection(JDBC_CONECTIN_STRING);
PreparedStatement stm = con.prepareStatement("DELETE FROM scores WHERE nick=? AND points=?");) {
for (Score score : scores) {
stm.setString(1, score.getName());
stm.setInt(2, score.getPoints());
stm.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void stop() {
/*nothing to do*/
}
}
......@@ -21,7 +21,7 @@ public class FileStorage implements ScoreStorageInterface {
if (Files.exists(Paths.get(SCORE_FILE_NAME))) {
try (Stream<String> lines = Files.lines(Paths.get(SCORE_FILE_NAME))) {
List<Score> result = lines.map(line -> line.split(";"))
.map(parts -> new Score(parts[0], Integer.parseInt(parts[1]))).toList();
.map(parts -> new Score(null, parts[0], Integer.parseInt(parts[1]), null)).toList();
return new ArrayList<>(result);
} catch (IOException e) {
e.printStackTrace();
......@@ -43,14 +43,33 @@ public class FileStorage implements ScoreStorageInterface {
}
@Override
public void insertScore(Score score) {
public Score save(Score score) {
List<Score> all = getAll();
all.add(score);
storeAll(all);
return score;
}
private void storeAll(List<Score> all) {
List<String> lines = all.stream().map(s -> String.format("%s;%d", s.getName(), s.getPoints())).toList();
try {
Files.write(Paths.get(SCORE_FILE_NAME), lines, StandardOpenOption.CREATE);
Files.write(Paths.get(SCORE_FILE_NAME), lines, StandardOpenOption.TRUNCATE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void delete(List<Score> scores) {
List<Score> all = getAll();
all.removeAll(scores);
storeAll(all);
}
@Override
public void stop() {
/*nothing to do*/
}
}
package lab.storage;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import lab.data.Score;
public class JpaConnector implements ScoreStorageInterface {
public JpaConnector() {
}
@Override
public List<Score> getAll() {
return Collections.emptyList();
}
@Override
public List<Score> getFirstTen() {
return Collections.emptyList();
}
@Override
public void init() {
}
@Override
public Score save(Score score) {
return null;
}
@Override
public void delete(List<Score> scores) {
}
@Override
public void stop() {
}
public Object getEntityManager() {
//return entity manager. Type Object is there because of compilation of empty task assignment
return null;
}
public Score find(long id) {
return null;
}
public void modifyNoPersistOrMerge(long id, Consumer<Score> motificator) {
}
}
......@@ -12,6 +12,10 @@ public interface ScoreStorageInterface {
void init();
void insertScore(Score score);
Score save(Score score);
void delete(List<Score> scores);
void stop();
}
\ No newline at end of file
module lab04_module {
module lab05_module {
requires transitive javafx.controls;
requires javafx.fxml;
requires javafx.base;
requires java.sql;
requires org.apache.logging.log4j;
requires static lombok;
requires com.h2database;
opens lab.gui to javafx.fxml;
opens lab.data to javafx.base;
......
......@@ -59,6 +59,7 @@
<columns>
<TableColumn fx:id="nickColumn" prefWidth="75.0" text="Nick" />
<TableColumn fx:id="pointsColumn" prefWidth="75.0" text="Points" />
<TableColumn fx:id="difficultColumn" prefWidth="75.0" text="Difficult" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
......@@ -67,6 +68,7 @@
<Button fx:id="btnGenerateScore" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#btnGenerateScoreAction" text="Generate new score" />
<Button fx:id="btnLoadAll" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#btnLoadAllAction" text="Load all from DB" />
<Button fx:id="btnLoadFirstTen" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#btnLoadFirstTenAction" text="Load first 10 from DB" />
<Button fx:id="btnDelete" layoutX="10.0" layoutY="312.0" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#btnDeleteAction" text="Delete" />
</children>
</VBox>
</children>
......
......@@ -2,85 +2,176 @@ package jez04.structure.test;
import static org.hamcrest.MatcherAssert.assertThat;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import java.util.regex.Pattern;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.api.TestMethodOrder;
import cz.vsb.fei.kelvin.unittest.SrcContains;
import cz.vsb.fei.kelvin.unittest.ProjectContains;
import cz.vsb.fei.kelvin.unittest.StructureHelper;
import cz.vsb.fei.kelvin.unittest.TextFileContains;
import cz.vsb.fei.kelvin.unittest.XmlFileContains;
import lab.data.Level;
import lab.data.Score;
import lab.storage.JpaConnector;
class ClassStructureTest {
StructureHelper helper = StructureHelper.getInstance(ClassStructureTest.class);
@Test
void lombokAsDependencyTest() throws URISyntaxException {
XmlFileContains xmlFileContains = new XmlFileContains("pom.xml", "/project/dependencies/dependency/artifactId[text() = 'lombok']");
Path root = TextFileContains.getProjectRoot(getClass());
assertThat(root, xmlFileContains);
}
@Test
void lombokAsAnnotationProcessorTest() throws URISyntaxException {
assertThat(TextFileContains.getProjectRoot(getClass()), new XmlFileContains("pom.xml", "/project/build/plugins/plugin/artifactId[text() = 'maven-compiler-plugin']"));
assertThat(TextFileContains.getProjectRoot(getClass()), new XmlFileContains("pom.xml", "/project/build/plugins/plugin/configuration/annotationProcessorPaths/path/artifactId[text() = 'lombok']"));
void jakartaAndHibernateAsDependencyTest() throws URISyntaxException {
assertThat(TextFileContains.getProjectRoot(getClass()), new XmlFileContains("pom.xml",
"/project/dependencies/dependency/artifactId[text() = 'jakarta.persistence-api']"));
assertThat(TextFileContains.getProjectRoot(getClass()), new XmlFileContains("pom.xml",
"/project/dependencies/dependency/artifactId[text() = 'hibernate-core']"));
}
@Test
void moduleInfoTest() throws URISyntaxException {
assertThat(TextFileContains.getProjectRoot(getClass()), new TextFileContains("module-info.java", "lombok;"));
void jakartaInModulInfoTest() throws URISyntaxException {
assertThat(TextFileContains.getProjectRoot(getClass()),
new TextFileContains("module-info.java", "jakarta.persistence;"));
assertThat(TextFileContains.getProjectRoot(getClass()),
new TextFileContains("module-info.java", "opens\\slab.data;"));
}
@CsvSource({
"Setup,@Getter,1",
"Setup,@.*Builder,1",
"Setup,@.*Default,3"
})
@ParameterizedTest(name = "useLombokTest in {1} annotation {2}")
void useLombokConfigTest(String className, String text, int count) throws URISyntaxException, ClassNotFoundException {
Class<?> config = helper.getClass(className);
assertThat(config, new SrcContains(text).count(count));
@Test
void pesistenceXmlTest() throws URISyntaxException {
ProjectContains projectContains = new ProjectContains("persistence.xml");
assertThat(TextFileContains.getProjectRoot(getClass()), projectContains);
assertThat(projectContains.getFoundFiles(), Matchers.not(Matchers.hasSize(0)));
Path persistenceXml = projectContains.getFoundFiles().getFirst();
assertThat(persistenceXml.toAbsolutePath().toString(),
Matchers.endsWith(Paths.get("resources", "META-INF", "persistence.xml").toString()));
}
@CsvSource({
"Score,@Setter,1",
"Score,@Getter,1",
"Score,@AllArgsConstructor,1",
"Score,@EqualsAndHashCode,1",
"Score,@ToString,1"
})
@ParameterizedTest(name = "useLombokTest in {1} annotation {2}")
void useLombokOrDataTest(String className, String text, int count) throws URISyntaxException, ClassNotFoundException {
Class<?> config = helper.getClass(className);
assertThat(config, Matchers.anyOf(new SrcContains(text).count(count),
new SrcContains("@Data").count(1)));
}
@Test
void recordTest() throws URISyntaxException {
long recordCount = Stream.concat(
helper.getAllNonInnerClasses().stream(),
helper.getAllInnerClasses().stream())
.filter(className -> !className.startsWith("javafx")).map(className -> {
try {
return helper.getClassDirectly(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}).filter(Objects::nonNull).filter(clazz -> clazz.isRecord()).count();
assertThat(recordCount, Matchers.greaterThanOrEqualTo(1L));
void useEnumeratedTest() throws URISyntaxException {
assertThat(TextFileContains.getProjectRoot(getClass()),
new TextFileContains("Score.java", "@Enumerated\\(\\s*EnumType.STRING\\s*\\)"));
}
@TestMethodOrder(OrderAnnotation.class)
@Nested
class JpaConnectorTests {
private Score template = new Score(null, "Tester", 100, Level.EASY);
private JpaConnector connector;
@BeforeEach
void init() {
connector = new JpaConnector();
}
@AfterEach
void cleanUp() {
connector.stop();
}
boolean same(Score s1, Score s2) {
return s1.getLevel() == s2.getLevel() && s1.getPoints() == s2.getPoints()
&& Objects.equals(s1.getName(), s2.getName());
}
@Test
@Order(100)
void jpaScoreInsertTest() {
Score savedScore = connector.save(template);
assertThat(savedScore.getId(), Matchers.notNullValue());
}
@Test
@Order(200)
void jpaScoreReadTest() {
List<Score> savedScores = connector.getAll().stream().filter(s -> same(s, template)).toList();
assertThat(savedScores, Matchers.not(Matchers.hasSize(0)));
}
@Test
@Order(250)
void jpaReadSortedTest() {
for (int i = 0; i < 20; i++) {
connector.save(template.toBuilder().points(i).build());
}
List<Score> result = connector.getFirstTen();
assertThat(result, Matchers.hasSize(10));
for (int i = 0; i < result.size()-1; i++) {
assertThat(result.get(i).getPoints(), Matchers.greaterThanOrEqualTo(result.get(i+1).getPoints()));
}
}
@Test
@Order(300)
void jpaScoreModifyTest() {
List<Score> savedScores = connector.getAll().stream().filter(s -> same(s, template)).toList();
List<Long> savedIds = savedScores.stream().map(Score::getId).toList();
assertThat(savedScores, Matchers.not(Matchers.hasSize(0)));
for (Score score : savedScores) {
score.setLevel(Level.HARD);
connector.save(score);
}
List<Score> modifiedScores = connector.getAll().stream().filter(s -> savedIds.contains(s.getId())).toList();
assertThat(modifiedScores, Matchers.not(Matchers.hasSize(0)));
List<Score> originalDataScores = connector.getAll().stream().filter(s -> same(s, template)).toList();
assertThat(originalDataScores, Matchers.hasSize(0));
}
@Test
@Order(400)
void jpaScoreDeleteTest() {
template.setLevel(Level.HARD);
List<Score> savedScores = connector.getAll().stream().filter(s -> same(s, template)).toList();
List<Long> savedIds = savedScores.stream().map(Score::getId).toList();
assertThat(savedScores, Matchers.not(Matchers.hasSize(0)));
connector.delete(savedScores);
List<Score> modifiedScores = connector.getAll().stream().filter(s -> savedIds.contains(s.getId())).toList();
assertThat(modifiedScores, Matchers.hasSize(0));
List<Score> originalDataScores = connector.getAll().stream().filter(s -> same(s, template)).toList();
assertThat(originalDataScores, Matchers.hasSize(0));
}
@Test
@Order(400)
void jpaMergeTest() {
connector.save(template);
connector.find(template.getId());
Score copy = template.toBuilder().points(500).build();
Score result = connector.save(copy);
assertThat(result, Matchers.not(Matchers.sameInstance(copy)));
}
@Test
@Order(500)
void jpamodifyNoPersistNoMergeTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, SecurityException {
template.setName("aaa");
connector.save(template);
connector.modifyNoPersistOrMerge(template.getId(), score -> score.setName("ok"));
Object em = connector.getEntityManager();
em.getClass().getMethod("clear").invoke(em);
assertThat(connector.find(template.getId()).getName(), Matchers.equalTo("ok"));
TextFileContains textFileContains = new TextFileContains("JpaConnector.java",
"void\\s+modifyNoPersistOrMerge[\\s\\S]*}").multiline(true);
assertThat(TextFileContains.getProjectRoot(ClassStructureTest.class), textFileContains);
Path score = textFileContains.getFoundFiles().getFirst();
String src = Files.readString(score);
String method = Pattern.compile("void\\s+modifyNoPersistOrMerge[\\s\\S]*}").matcher(src).results().findFirst().get().group();
assertThat(method, Matchers.not(Matchers.containsString("persist")));
assertThat(method, Matchers.not(Matchers.containsString("merge")));
}
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment