diff --git a/scores.csv b/scores.csv index 1ed1b37d391570a7e589bc3b6c42ca305f4a5207..b6a22127374dccd38525c942ef23f2ae1712ac62 100644 --- a/scores.csv +++ b/scores.csv @@ -4,3 +4,8 @@ TikTokTornado;210;EASY ScriptSensei;158;EASY SnapMasteraaa;167000;null ScriptSenseidddd;1580000;null +TikTokTornado;210;EASY +ScriptSensei;158000;MEDIUM +TikTokTornado;210000;EASY +TikTokTornado;210;EASY +ScriptSensei;158;EASY diff --git a/src/main/java/lab/MyEdit.java b/src/main/java/lab/MyEdit.java new file mode 100644 index 0000000000000000000000000000000000000000..07b46a578c37fea172e98f2dcb3e3d94c6d08755 --- /dev/null +++ b/src/main/java/lab/MyEdit.java @@ -0,0 +1,13 @@ +package lab; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface MyEdit { + public boolean readOnly() default false; + public boolean visible() default true; +} diff --git a/src/main/java/lab/data/Score.java b/src/main/java/lab/data/Score.java index c6219a5ccf95cecfdc1eeafa96b3ae07ef94f928..137e6fb71599e58e3b523cac9a1b0efa2cd562db 100644 --- a/src/main/java/lab/data/Score.java +++ b/src/main/java/lab/data/Score.java @@ -8,7 +8,7 @@ import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; - +import lab.MyEdit; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; @@ -31,11 +31,19 @@ public class Score { @Id @GeneratedValue(strategy = GenerationType.AUTO) + @MyEdit(visible = false) private Long id; + @MyEdit(readOnly = true) private String name; + @MyEdit private int points; @Enumerated(EnumType.STRING) + @MyEdit private Level level; + + public String getVirtual(){ + return "aaaa"; + } public static Score generate() { diff --git a/src/main/java/lab/gui/EditController.java b/src/main/java/lab/gui/EditController.java index 44e7355f3c71319588e2889aa8398ced095f35f8..abb90802bb0640a8206f4a121cc0d3401e956f03 100644 --- a/src/main/java/lab/gui/EditController.java +++ b/src/main/java/lab/gui/EditController.java @@ -4,42 +4,25 @@ import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; -import java.io.IOException; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.ResourceBundle; 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.geometry.HPos; -import javafx.scene.Group; -import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.control.SelectionMode; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableRow; -import javafx.scene.control.TableView; import javafx.scene.control.TextField; -import javafx.scene.control.ToggleButton; -import javafx.scene.control.ToggleGroup; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.input.MouseButton; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.GridPane; -import javafx.scene.text.Text; -import javafx.stage.Modality; import javafx.stage.Stage; -import javafx.stage.StageStyle; -import lab.Setup; +import lab.MyEdit; import lab.data.Score; -import lab.game.Difficult; import lombok.Setter; /** @@ -75,9 +58,52 @@ public class EditController { @FXML void btnOkAction(ActionEvent event) { - //TODO: add code to read values from text fields - - mainScreenController.updateData((Score)data); + try { + BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass()); + for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) { + try { + String name = propertyDescriptor.getName(); + Field field = data.getClass().getDeclaredField(name); + MyEdit myEdit = field.getAnnotation(MyEdit.class); + if(myEdit == null || !myEdit.visible() || myEdit.readOnly()) { + continue; + } + TextField textField = nameToTextField.get(name); + String stringValue = textField.getText(); + Object value = null; + if (propertyDescriptor.getPropertyType() == String.class) { + value = stringValue; + } else if (stringValue == null || stringValue.isBlank()) { + value = null; + } else if (propertyDescriptor.getPropertyType() == Integer.class + || propertyDescriptor.getPropertyType() == int.class) { + value = Integer.parseInt(stringValue); + } else if (propertyDescriptor.getPropertyType() == Long.class + || propertyDescriptor.getPropertyType() == long.class) { + value = Long.parseLong(stringValue); + } else if (propertyDescriptor.getPropertyType() == Double.class + || propertyDescriptor.getPropertyType() == double.class) { + value = Double.parseDouble(stringValue); + } else if (propertyDescriptor.getPropertyType().isEnum()) { + for (Object enumConstant : propertyDescriptor.getPropertyType().getEnumConstants()) { + if (enumConstant.toString().equalsIgnoreCase(stringValue)) { + value = enumConstant; + break; + } + } + } + if (propertyDescriptor.getWriteMethod() != null) { + propertyDescriptor.getWriteMethod().invoke(data, value); + } + } catch (NoSuchFieldException e) { + log.warn("property {} has no field", propertyDescriptor.getName()); + } + } + } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) { + log.error(e); + } + + mainScreenController.updateData((Score) data); stage.hide(); } @@ -93,15 +119,41 @@ public class EditController { public void setObjectToEdit(Object data) { this.data = data; - log.info("data set {}", data); + log.info("data set {}", data); content.getChildren().clear(); - - //TODO: code to detect properties and add rows to dialog + ResourceBundle msg = ResourceBundle.getBundle("msg"); + try { + BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass()); + int row = 0; + for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) { + try { + Field field = data.getClass().getDeclaredField(propertyDescriptor.getName()); + MyEdit myEdit = field.getAnnotation(MyEdit.class); + if (myEdit == null) { + continue; + } + if (!myEdit.visible()) { + continue; + } + String name = propertyDescriptor.getName(); + Object value = propertyDescriptor.getReadMethod().invoke(data); + String localizedName = msg.getString(String.format("%s.%s", data.getClass().getSimpleName(), field.getName())); + addDialogRow(row, name, localizedName, value != null ? value.toString() : "", !myEdit.readOnly()); + row++; + } catch (NoSuchFieldException e) { + log.warn("property '{}' has no field in class '{}'", propertyDescriptor.getName(), data.getClass().getCanonicalName()); + } + } + } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) { + log.error(e); + } } - private void addDialogRow(int rowNumber, String name, String descriptionName, String stringValue) { + private void addDialogRow(int rowNumber, String name, String descriptionName, String stringValue, + boolean editable) { Label label = new Label(descriptionName); TextField textField = new TextField(stringValue); + textField.setEditable(editable); nameToTextField.put(name, textField); content.addRow(rowNumber, label, textField); GridPane.setHalignment(label, HPos.RIGHT); diff --git a/src/main/resources/msg.properties b/src/main/resources/msg.properties new file mode 100644 index 0000000000000000000000000000000000000000..d0655f9afcee79b8fb8c5a4087d2d2b38292a47d --- /dev/null +++ b/src/main/resources/msg.properties @@ -0,0 +1,5 @@ + +Score.id = id +Score.level = level +Score.name = name +Score.points = points diff --git a/src/main/resources/msg_cs.properties b/src/main/resources/msg_cs.properties new file mode 100644 index 0000000000000000000000000000000000000000..e397613b223ead974bf36a1be69512c88c897149 --- /dev/null +++ b/src/main/resources/msg_cs.properties @@ -0,0 +1,5 @@ + +Score.id = Jednozna\u010Dn\u00FD identifik\u00E1tor +Score.level = Obt\u00ED\u017Enost +Score.name = Jm\u00E9no +Score.points = Body diff --git a/src/main/resources/msg_cs_CZ.properties b/src/main/resources/msg_cs_CZ.properties new file mode 100644 index 0000000000000000000000000000000000000000..73f926b653417019db394ae6ac1b0e5a9d5a56ae --- /dev/null +++ b/src/main/resources/msg_cs_CZ.properties @@ -0,0 +1,4 @@ +#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) + +Score.id = Jedine\u010Dn\u00FD identifik\u00E1tor +Score.name = N\u00E1zev diff --git a/src/main/resources/msg_en.properties b/src/main/resources/msg_en.properties new file mode 100644 index 0000000000000000000000000000000000000000..83da6ab577a70580f3495e7d495641b54b61e490 --- /dev/null +++ b/src/main/resources/msg_en.properties @@ -0,0 +1,6 @@ +#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) + +Score.id = Unique identificator +Score.level = Level +Score.name = Name +Score.points = Points diff --git a/src/test/java/jez04/structure/test/ClassStructureTest.java b/src/test/java/jez04/structure/test/ClassStructureTest.java index 21c3023a1e362b7c8191843eee2cbda6fabde061..2e2be8bd0437f77356df9882a1e0553ec44d8a03 100644 --- a/src/test/java/jez04/structure/test/ClassStructureTest.java +++ b/src/test/java/jez04/structure/test/ClassStructureTest.java @@ -2,176 +2,40 @@ 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.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.api.TestMethodOrder; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; -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 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']")); + // @formatter:off + @ParameterizedTest + @CsvSource({ + "Score.java,@MyEdit,4", + "EditController.java,Introspector.getBeanInfo,1", + "EditController.java,BeanInfo,1", + "EditController.java,PropertyDescriptor,1", + "EditController.java,getDeclaredField,1", + "EditController.java,getAnnotation,1", + "EditController.java,\\.readOnly\\(\\),1", + "EditController.java,\\.visible\\(\\),1", + "MyEdit.java,@interface,1", + "MyEdit.java,@Retention,1", + "MyEdit.java,RetentionPolicy\\.RUNTIME,1", + "MyEdit.java,readOnly\\(\\),1", + "MyEdit.java,visible\\(\\),1", + "msg(_.*)?\\.properties,points,2", + }) + // @formatter:on + void anotaceTest(String file, String annotation, int count) throws URISyntaxException { + assertThat(TextFileContains.getProjectRoot(getClass()), new TextFileContains(file, annotation).count(count).useRegExpForName(true)); } - @Test - 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;")); - } - - @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())); - } - - @Test - 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"))); - } - } }