diff --git a/.gitignore b/.gitignore index db44c8a723e017349e18633821c8ee475d15621c..e312b71d02d3f54f62aca00dc444cc54b0116b9e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .settings/ .project .classpath +*.mv.db +*.trace.db diff --git a/pom.xml b/pom.xml index 359f0cc99b2600a7b82feea61040de8ee3debcd9..1a82f23a0d2b192a13b70ea30d6b9f35caf7b9e3 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,13 @@ <maven.compiler.target>21</maven.compiler.target> </properties> <dependencies> + <!-- https://mvnrepository.com/artifact/com.h2database/h2 --> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>2.3.232</version> + </dependency> + <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> diff --git a/src/main/java/lab/DbConnector.java b/src/main/java/lab/DbConnector.java new file mode 100644 index 0000000000000000000000000000000000000000..9aba4e9d79f07386e26e3b024bde198495a235b2 --- /dev/null +++ b/src/main/java/lab/DbConnector.java @@ -0,0 +1,57 @@ +package lab; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +public class DbConnector { + + private static final String JDBC_CONECTIN_STRING = "jdbc:h2:file:./scoreDB"; + + public static List<Score> getAll() { + return queryScore("select * from scores;"); + } + + public static List<Score> getFirstTen() { + return queryScore("select * from scores order by points desc limit 10;"); + } + + private static List<Score> queryScore(String query) { + List<Score> result = new ArrayList<>(); + try (Connection con = DriverManager.getConnection(JDBC_CONECTIN_STRING); + Statement stm = con.createStatement(); + ResultSet rs = stm.executeQuery(query);) { + while (rs.next()) { + result.add(new Score(rs.getString("nick"), rs.getInt("points"))); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return result; + } + + public static void createTable() { + try (Connection con = DriverManager.getConnection(JDBC_CONECTIN_STRING); + Statement stm = con.createStatement();) { + stm.executeUpdate("CREATE TABLE if not exists scores (nick VARCHAR(50) NOT NULL, points INT NOT NULL);"); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public static void insertScore(Score score) { + try (Connection con = DriverManager.getConnection(JDBC_CONECTIN_STRING); + PreparedStatement stm = con.prepareStatement("INSERT INTO scores VALUES (?, ?)");) { + stm.setString(1, score.getName()); + stm.setInt(2, score.getPoints()); + stm.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/lab/GameController.java b/src/main/java/lab/GameController.java index 35211711b0769e81d4f543db4d9eb0d147efd3ce..593d69437bf42df49907ffb9bd68815e4e5229da 100644 --- a/src/main/java/lab/GameController.java +++ b/src/main/java/lab/GameController.java @@ -1,14 +1,30 @@ package lab; +import java.util.List; +import java.util.stream.Stream; + import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.geometry.Point2D; 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 javafx.scene.control.cell.PropertyValueFactory; public class GameController { + @FXML + private Button btnGenerateScore; + + @FXML + private Button btnLoadAll; + + @FXML + private Button btnLoadFirstTen; + @FXML private Slider angle; @@ -18,6 +34,15 @@ public class GameController { @FXML private Canvas canvas; + @FXML + private TableView<Score> scores; + @FXML + private TableColumn<Score, String> nickColumn; + + @FXML + private TableColumn<Score, Integer> pointsColumn; + + private DrawingThread timer; @FXML @@ -42,6 +67,28 @@ public class GameController { () -> System.out.println("au!!!!")); } + @FXML + void btnGenerateScoreAction(ActionEvent event) { + Score score = Score.generate(); + this.scores.getItems().add(score); + DbConnector.insertScore(score); + } + + @FXML + void btnLoadAllAction(ActionEvent event) { + updateScoreTable(DbConnector.getAll()); + } + + @FXML + void btnLoadFirstTenAction(ActionEvent event) { + updateScoreTable(DbConnector.getFirstTen()); + } + + private void updateScoreTable(List<Score> scores) { + this.scores.getItems().clear(); + this.scores.getItems().addAll(scores); + } + private void updateHits() { hits.setText(String.format("Hit count: %03d", hitcount)); } @@ -61,6 +108,20 @@ public class GameController { angle.valueProperty().addListener( (observable, oldValue, newValue) -> timer.getWorld().getCannon().setAngle(newValue.doubleValue())); + + nickColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + pointsColumn.setCellValueFactory(new PropertyValueFactory<>("points")); + + initDB(); + + } + + private void initDB() { + scores.getItems().addAll(Stream.generate(Score::generate).limit(10).toList()); + DbConnector.createTable(); + DbConnector.getAll(); + + } public void stop() { diff --git a/src/main/java/lab/Score.java b/src/main/java/lab/Score.java new file mode 100644 index 0000000000000000000000000000000000000000..70d137ac13f30db887a2a8be3cbfafd2ff41ee73 --- /dev/null +++ b/src/main/java/lab/Score.java @@ -0,0 +1,64 @@ +package lab; + +import java.util.Random; + +public class Score { + + private static final Random RANDOM = new Random(); + + private String name; + private int points; + + public Score(String name, int points) { + this.name = name; + this.points = points; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getPoints() { + return points; + } + + public void setPoints(int points) { + this.points = points; + } + + @Override + public String toString() { + return "Score [name=" + name + ", points=" + points + "]"; + } + + public static Score generate() { + return new Score(getRandomNick(), RANDOM.nextInt(50, 300)); + } + + public static final String[] nicks = { "CyberSurfer", "PixelPioneer", "SocialSavvy", "DigitalDynamo", "ByteBuddy", "InstaGuru", + "TikTokTornado", "SnapMaster", "TweetTrendsetter", "ChatChampion", "HashtagHero", "EmojiEnthusiast", + "StoryStylist", "SelfieStar", "FilterFanatic", "VlogVirtuoso", "Memelord", "InfluencerInsider", + "StreamSupreme", "GeekyGizmo", "CodeCommander", "JavaJuggernaut", "ByteNinja", "SyntaxSamurai", + "ClassyCoder", "ObjectOmnipotent", "LoopLegend", "VariableVirtuoso", "DebugDemon", "CompilerCrusader", + "PixelProdigy", "VirtualVoyager", "AlgorithmAce", "DataDynamo", "ExceptionExpert", "BugBuster", + "SyntaxSorcerer", "CodeCrusader", "JavaJester", "NerdyNavigator", "CryptoCaptain", "SocialButterfly", + "AppArchitect", "WebWizard", "FunctionFreak", "PixelArtist", "CyberPhantom", "HackHero", "CacheChampion", + "ScreenSage", "WebWeaver", "LogicLover", "BitBlazer", "NetworkNomad", "ProtocolPioneer", "BinaryBoss", + "StackSultan", "SocialScribe", "RenderRuler", "ScriptSorcerer", "HTMLHero", "PixelProwler", "FrameFreak", + "DataDreamer", "BotBuilder", "ByteBishop", "KeyboardKnight", "DesignDaredevil", "JavaJuggler", + "SyntaxStrategist", "TechTactician", "ProgramProdigy", "BinaryBard", "PixelPoet", "GigabyteGuru", + "TechTrekker", "NetworkNinja", "DataDetective", "MatrixMaster", "CodeConductor", "AppAlchemist", + "ServerSage", "ClusterChampion", "ScriptSensei", "KeyboardKicker", "CacheCrafter", "SocialSpark", + "BinaryBeast", "CodeConnoisseur", "BitBrain", "VirtualVanguard", "SystemSculptor", "RenderRogue", + "CryptoConqueror", "MachineMonarch", "PixelPal", "CompilerCaptain", "BitBuilder", "TechTitan", + "CloudConqueror", "EchoExplorer", "FunctionFanatic", "RobotRanger" }; + + public static String getRandomNick() { + return nicks[RANDOM.nextInt(nicks.length)]; + } + +} diff --git a/src/main/java/lab/Ufo.java b/src/main/java/lab/Ufo.java index c55bfd776d67b082e28ce5d4a79c34425fca5f5a..013939b2da94e8eaf7502bda40d9b0f9640adce3 100644 --- a/src/main/java/lab/Ufo.java +++ b/src/main/java/lab/Ufo.java @@ -10,7 +10,7 @@ import javafx.scene.image.Image; public class Ufo extends WorldEntity implements Collisionable { private static final Random RANDOM = new Random(); - private Image image = new Image(this.getClass().getResourceAsStream("ufo-small.gif")); + private Image image; private Point2D velocity; public Ufo(World world) { @@ -23,9 +23,17 @@ public class Ufo extends WorldEntity implements Collisionable { this.velocity = velocity; } + private Image getImage() { + if (image == null) { + image = new Image(Ufo.class.getResourceAsStream("ufo-small.gif")); + } + return image; + + } + @Override public void drawInternal(GraphicsContext gc) { - gc.drawImage(image, getPosition().getX(), getPosition().getY()); + gc.drawImage(getImage(), getPosition().getX(), getPosition().getY()); } public void changeDirection() { @@ -36,14 +44,14 @@ public class Ufo extends WorldEntity implements Collisionable { public void simulate(double deltaT) { position = position.add(velocity.multiply(deltaT)); position = new Point2D(position.getX() % world.getWidth(), position.getY()); - if(position.getX() < -image.getWidth()) { + if (position.getX() < -getImage().getWidth()) { position = new Point2D(world.getWidth(), position.getY()); } } @Override public Rectangle2D getBoundingBox() { - return new Rectangle2D(position.getX(), position.getY(), image.getWidth(), image.getHeight()); + return new Rectangle2D(position.getX(), position.getY(), getImage().getWidth(), getImage().getHeight()); } @Override @@ -53,7 +61,7 @@ public class Ufo extends WorldEntity implements Collisionable { @Override public void hitBy(Collisionable another) { - if(another instanceof BulletAnimated || another instanceof Bullet) { + if (another instanceof BulletAnimated || another instanceof Bullet) { world.remove(this); } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 736971c46e3a0601a2db674231080b0e8f886cc0..51b9b1c06e53e56d996d38195f8a878b7315b6db 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -2,6 +2,7 @@ module lab01 { requires transitive javafx.controls; requires javafx.fxml; requires javafx.base; + requires java.sql; opens lab to javafx.fxml; exports lab; } \ No newline at end of file diff --git a/src/main/resources/lab/gameWindow.fxml b/src/main/resources/lab/gameWindow.fxml index c2b94f192a11c3e3bd9bc79f608fae84cc19f2ed..7fc092a6f5db50316afebcd6bbdb4bdcd87ba6c1 100644 --- a/src/main/resources/lab/gameWindow.fxml +++ b/src/main/resources/lab/gameWindow.fxml @@ -6,11 +6,14 @@ <?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 javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.VBox?> <?import javafx.scene.text.Font?> -<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="lab.GameController"> +<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="471.0" prefWidth="901.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="lab.GameController"> <bottom> <HBox alignment="TOP_CENTER" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER"> <children> @@ -43,4 +46,22 @@ <top> <Label fx:id="hits" text="Hit count: 0" textAlignment="CENTER" BorderPane.alignment="CENTER" /> </top> + <right> + <VBox maxHeight="1.7976931348623157E308" maxWidth="-Infinity" BorderPane.alignment="CENTER"> + <children> + <TableView fx:id="scores" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"> + <columns> + <TableColumn fx:id="nickColumn" prefWidth="75.0" text="Nick" /> + <TableColumn fx:id="pointsColumn" prefWidth="75.0" text="Points" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + </TableView> + <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" /> + </children> + </VBox> + </right> </BorderPane> diff --git a/src/test/java/jez04/structure/test/ClassStructureTest.java b/src/test/java/jez04/structure/test/ClassStructureTest.java index 865fd162dbc7f658888bac01c115c843ad0ca6e0..b54aa04a2ab7894f6573cdf0fbfe972b9f0b3305 100644 --- a/src/test/java/jez04/structure/test/ClassStructureTest.java +++ b/src/test/java/jez04/structure/test/ClassStructureTest.java @@ -2,38 +2,16 @@ package jez04.structure.test; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.FileVisitResult; -import java.nio.file.FileVisitor; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.reflections.Configuration; -import org.reflections.Reflections; -import org.reflections.scanners.Scanners; -import org.reflections.util.ConfigurationBuilder; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; class ClassStructureTest { @@ -52,82 +30,72 @@ class ClassStructureTest { } @Test - void gameControllerActionMethodTest() { + void gameControllerButtonActionMethodTest() { helper.classExist("GameController"); Class<?> c = helper.getClass("GameController"); - helper.hasMethodRegexp(c, ".*", void.class, ActionEvent.class); + long count = helper.countMethodRegexp(c, ".*", void.class, ActionEvent.class); + assertTrue(count > 1, "Only " + count+ " method handling button click found. Expected more then 1"); } @Test - void gameControllerLambdasTest() { + void gameControllerTableViewTest() { helper.classExist("GameController"); Class<?> c = helper.getClass("GameController"); - long lamdaCount = helper.countMethodRegexp(c, "lambda\\$.*"); - long innerClasscount = helper.countClassesRegexp(".*GameController\\$.*"); - assertTrue(lamdaCount + innerClasscount >= 2, - "At least 2 inner classes or lamdas required for GameController but only " - + (lamdaCount + innerClasscount) + " found."); + helper.hasProperty(c, ".*", TableView.class, false); } - + @Test - void hitListenerExistenceTest() { - helper.classExist("HitListener"); - } - - @Test - void hitListenerEventMethodTest() { - helper.classExist("HitListener"); - Class<?> c = helper.getClass("HitListener"); - helper.hasMethod(c, "ufoDestroyed"); + void gameControllerTableColumnTest() { + helper.classExist("GameController"); + Class<?> c = helper.getClass("GameController"); + helper.hasProperty(c, ".*", TableColumn.class, false); } @Test - void sceneCollectionTest() { - helper.classExist("World"); - Class<?> c = helper.getClass("World"); - long collectionCount = Arrays.asList(c.getDeclaredFields()).stream() - .filter(f -> Collection.class.isAssignableFrom(f.getType())).count(); - assertTrue(collectionCount >= 3, "lab.Scene require atleast 3 filed of type/subtype Collection, but only " - + collectionCount + " found."); + void dbConnectorTest() { + helper.classExistRegexp(".*[Dd][Bb][cC]on.*"); } @Test - void worldMethodAddTest() { - helper.classExist("World"); - Class<?> c = helper.getClass("World"); - helper.hasMethodRegexp(c, "add.*", void.class, helper.getClass("DrawableSimulable")); - ; + void dbConnectorGetAllTest() { + helper.classExistRegexp(".*[Dd][Bb][cC]on.*"); + Class<?> scoreClass = helper.getClassRegexp(".*[sS]core.*"); + Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*"); + helper.hasMethodRegexp(c, ".*[aA]ll.*", List.class); + helper.hasMethodRegexp(c, ".*[iI]nsert.*", void.class, scoreClass); } - @Test - void worldMethodRemoveTest() { - helper.classExist("World"); - Class<?> c = helper.getClass("World"); - helper.hasMethodRegexp(c, "remove.*", void.class, helper.getClass("DrawableSimulable")); - ; + void dbConnectorInsertTest() { + helper.classExistRegexp(".*[Dd][Bb][cC]on.*"); + Class<?> scoreClass = helper.getClassRegexp(".*[sS]core.*"); + Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*"); + helper.hasMethodRegexp(c, ".*[iI]nsert.*", void.class, scoreClass); } - @Test - void bulletAnimatedMethodAddTest() { - helper.classExist("BulletAnimated"); - Class<?> c = helper.getClass("BulletAnimated"); - Class<?> l = helper.getClass("HitListener"); - helper.hasMethodRegexp(c, "add.*", List.of(void.class, boolean.class), l); + void dbConnectorContainsJdbcTest() throws URISyntaxException, IOException { + helper.classExistRegexp(".*[Dd][Bb][cC]on.*"); + Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*"); + String src = helper.getSourceCode(c); + assertTrue(src.contains("jdbc:"), "No usage of jdbc detect."); } @Test - void bulletAnimatedMethodRemoveTest() { - helper.classExist("Ufo"); - Class<?> c = helper.getClass("BulletAnimated"); - Class<?> l = helper.getClass("HitListener"); - helper.hasMethodRegexp(c, "remove.*", List.of(void.class, boolean.class), l); + void dbConnectorContainsDriverManagerTest() throws URISyntaxException, IOException { + helper.classExistRegexp(".*[Dd][Bb][cC]on.*"); + Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*"); + String src = helper.getSourceCode(c); + assertTrue(src.contains("DriverManager"), "No usage of DriverManager detect."); } @Test - void bulletAnimatedMethodFireTest() { - helper.classExist("BulletAnimated"); - Class<?> c = helper.getClass("BulletAnimated"); - assertTrue(helper.countMethodRegexp(c, "fire.*") > 0, "Method fire.* in LochNess not found."); + void dbConnectorContainsSqlTest() throws URISyntaxException, IOException { + helper.classExistRegexp(".*[Dd][Bb][cC]on.*"); + Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*"); + String src = helper.getSourceCode(c).toLowerCase(); + assertTrue(src.contains("create "), "No usage of SQL create."); + assertTrue(src.contains("select "), "No usage of SQL select."); + assertTrue(src.contains("insert "), "No usage of SQL table."); + assertTrue(src.contains(" from "), "No usage of SQL from."); + assertTrue(src.contains(" table"), "No usage of SQL table."); } - } diff --git a/src/test/java/jez04/structure/test/StructureHelper.java b/src/test/java/jez04/structure/test/StructureHelper.java index aa10c81e0b4ccbc5d8bd405731e00501a30f8c27..f3aed5d82d5483fad6c3733ace46c8ce5c29e785 100644 --- a/src/test/java/jez04/structure/test/StructureHelper.java +++ b/src/test/java/jez04/structure/test/StructureHelper.java @@ -1,14 +1,17 @@ package jez04.structure.test; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.FileVisitResult; @@ -19,10 +22,12 @@ import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.TreeSet; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.junit.jupiter.api.Assertions; @@ -43,11 +48,31 @@ class StructureHelper { assertTrue(allClasses.stream().anyMatch(c -> c.endsWith(name)), "Class/Interface " + name + " not found"); } + public void classExistRegexp(String name) { + assertTrue(allClasses.stream().anyMatch(c -> c.matches(name)), "Class/Interface " + name + " not found"); + } + + public Class<?> getClassDirectly(String name) { + return loadClass(name, name); + } + + public Class<?> getClassRegexp(String name) { + String className = allClasses.stream().filter(c -> c.matches(name)).findAny().orElse(null); + if (className == null) { + Assertions.fail("Class " + name + " not found."); + } + return loadClass(name, className); + } + public Class<?> getClass(String name) { String className = allClasses.stream().filter(c -> c.endsWith(name)).findAny().orElse(null); if (className == null) { Assertions.fail("Class " + name + " not found."); } + return loadClass(name, className); + } + + private Class<?> loadClass(String name, String className) { try { return Class.forName(className); } catch (ClassNotFoundException e) { @@ -108,48 +133,87 @@ class StructureHelper { + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); } + public Method getMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) { + List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); + List<Method> foundMethods = methods.stream().filter(m -> m.getName().contains(methodName)) + .filter(m -> m.getReturnType().equals(returnType)) + .filter(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))).toList(); + if (foundMethods.isEmpty()) { + fail("No method " + methodName + " found"); + } + if (foundMethods.size() > 1) { + fail("More then one method " + methodName + " found"); + } + return foundMethods.get(0); + } + public long countMethodRegexp(Class<?> interfaceDef, String methodNameRegexp) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); return methods.stream().filter(m -> m.getName().matches(methodNameRegexp)).count(); } + + public long countMethodReference(Class<?> interfaceDef) throws URISyntaxException, IOException { + Pattern p = Pattern.compile("::"); + Matcher m = p.matcher(getSourceCode(interfaceDef)); + return m.results().count(); + } + + public long countMethodReferenceOn(Class<?> interfaceDef, String to) { + try { + Pattern p = Pattern.compile(to + "::"); + Matcher m = p.matcher(getSourceCode(interfaceDef)); + return m.results().count(); + } catch (URISyntaxException | IOException e) { + e.printStackTrace(); + return 0; + } + } + public long countClassesRegexp(String classNameRegexp) { return getNameOfAllClasses().stream().filter(className -> className.matches(classNameRegexp)).count(); } + public void hasConstructor(Class<?> classDef, Class<?>... params) { + getConstructor(classDef, params); + } + + public Constructor<?> getConstructor(Class<?> classDef, Class<?>... params) { + List<Constructor<?>> constructors = Arrays.asList(classDef.getConstructors()); + List<Constructor<?>> foundConstructors = constructors.stream() + .filter(m -> m.getParameterCount() == params.length) + .filter(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))).toList(); + if (foundConstructors.isEmpty()) { + fail("No constructor found with parameters: " + + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); + } + if (foundConstructors.size() > 1) { + fail("More then one constructor found with parameters: " + + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); + } + return foundConstructors.get(0); + } + public void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType, Class<?>... params) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); assertTrue(methods.stream().anyMatch(m -> m.getName().matches(methodNameRegexp)), "No method " + methodNameRegexp); assertTrue( - methods.stream().filter( - m -> - m.getName().matches(methodNameRegexp)) - .filter( - m -> - m.getReturnType().equals(returnType)) - .anyMatch(m -> - Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))), + methods.stream().filter(m -> m.getName().matches(methodNameRegexp)) + .filter(m -> m.getReturnType().equals(returnType)) + .anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))), "Method " + methodNameRegexp + " has no all parrams:" + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); } - public void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Collection<Class<?>> returnTypeOnOf, + public long countMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType, Class<?>... params) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); assertTrue(methods.stream().anyMatch(m -> m.getName().matches(methodNameRegexp)), "No method " + methodNameRegexp); - assertTrue( - methods.stream().filter( - m -> - m.getName().matches(methodNameRegexp)) - .filter( - m -> - returnTypeOnOf.contains(m.getReturnType())) - .anyMatch(m -> - Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))), - "Method " + methodNameRegexp + " has no all parrams:" - + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); + return methods.stream().filter(m -> m.getName().matches(methodNameRegexp)) + .filter(m -> m.getReturnType().equals(returnType)) + .filter(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))).count(); } public void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName, @@ -179,16 +243,34 @@ class StructureHelper { "Class " + clazz.getName() + " not extends class " + parentName); } + public void hasExtends(Class<?> clazz, Class<?> parent) { + assertTrue(clazz.getSuperclass().equals(parent), + "Class " + clazz.getName() + " not extends class " + parent.getCanonicalName()); + } + public void hasMethod(Class<?> interfaceDef, String methodName) { List<Method> methods = Arrays.asList(interfaceDef.getMethods()); assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName); } + public String getSourceCode(Class<?> clazz) throws URISyntaxException, IOException { + URL myClassUrl = StructureHelper.class.getResource(this.getClass().getSimpleName() + ".class"); + Path classRoot = Paths.get(myClassUrl.toURI()); + while (!"test-classes".equals(classRoot.getFileName().toString()) + && !"classes".equals(classRoot.getFileName().toString())) { + classRoot = classRoot.getParent(); + } + Path srcRoot = classRoot.getParent().getParent().resolve(Paths.get("src", "main", "java")); + System.out.println("class root: " + classRoot); + Path srcPath = srcRoot.resolve(clazz.getCanonicalName().replace(".", File.separator) + ".java"); + return Files.readString(srcPath); + } + public Set<String> getNameOfAllClasses() { - List<String> initClassesName = new ArrayList<>(); - dynamicaliFoundSomeClass(initClassesName); - initClassesName.addAll(List.of("lab.Routines", "lab.App", "lab.DrawingThread")); - for (String className : initClassesName) { + Set<String> allClassesName = new TreeSet<>(); + dynamicalyFoundSomeClass(allClassesName); +// allClassesName.addAll(List.of("cz.vsb.fei.lab.App", "lab.Routines", "lab.App", "lab.DrawingThread")); + for (String className : allClassesName) { try { Class.forName(className); break; @@ -196,7 +278,6 @@ class StructureHelper { System.out.println(String.format("Class '%s' cannot be loaded: %s", className, e.getMessage())); } } - Set<String> allClasses = new HashSet<>(); for (Package p : Package.getPackages()) { if (p.getName().startsWith("java.") || p.getName().startsWith("com.") || p.getName().startsWith("jdk.") || p.getName().startsWith("javafx.") || p.getName().startsWith("org.") @@ -204,36 +285,39 @@ class StructureHelper { || p.getName().startsWith("javassist")) { continue; } - System.out.println(p.getName()); +// System.out.println(p.getName()); Configuration conf = new ConfigurationBuilder().addScanners(Scanners.SubTypes.filterResultsBy(pc -> true)) .forPackages(p.getName()); Reflections reflections = new Reflections(conf); - allClasses.addAll(reflections.getAll(Scanners.SubTypes.filterResultsBy(c -> { - System.out.println(c); + allClassesName.addAll(reflections.getAll(Scanners.SubTypes.filterResultsBy(c -> { +// System.out.println(">>> " + c); return true; }))); } - for (String string : allClasses) { - System.out.println(string); - } - return allClasses; + return allClassesName; } - public void dynamicaliFoundSomeClass(List<String> initClassesName) { - URL myClassUrl = StructureHelper.class.getResource("ClassStructureTest.class"); - myClassUrl.getFile(); + private static final List<String> dirsToSkip = List.of("jez04", "META-INF"); + private static final List<String> filesToSkip = List.of("module-info.class"); + + public void dynamicalyFoundSomeClass(Set<String> allClassesName) { + URL myClassUrl = StructureHelper.class.getResource(this.getClass().getSimpleName() + ".class"); try { - Path classRoot = Paths.get(myClassUrl.toURI()).getParent().getParent().getParent().getParent(); + Path classRoot = Paths.get(myClassUrl.toURI()); + while (!"test-classes".equals(classRoot.getFileName().toString()) + && !"classes".equals(classRoot.getFileName().toString())) { + classRoot = classRoot.getParent(); + } if ("test-classes".equals(classRoot.getFileName().toString())) { classRoot = classRoot.getParent().resolve("classes"); } - System.out.println("class root: " + classRoot); +// System.out.println("class root: " + classRoot); final Path classRootFinal = classRoot; Files.walkFileTree(classRoot, new FileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (List.of("jez04", "META-INF").contains(dir.getFileName().toString())) { + if (dirsToSkip.contains(dir.getFileName().toString())) { return FileVisitResult.SKIP_SUBTREE; } return FileVisitResult.CONTINUE; @@ -241,18 +325,20 @@ class StructureHelper { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - System.out.println("VISIT: " + file); - if ("module-info.class".equals(file.getFileName().toString())) { + if (filesToSkip.contains(file.getFileName().toString())) { return FileVisitResult.CONTINUE; } if (!file.getFileName().toString().endsWith(".class")) { return FileVisitResult.CONTINUE; } + if (file.getFileName().toString().contains("$")) { + return FileVisitResult.CONTINUE; + } String foundClassName = classRootFinal.relativize(file).toString(); foundClassName = foundClassName.substring(0, foundClassName.length() - 6) .replace(File.separatorChar, '.'); - initClassesName.add(foundClassName); - return FileVisitResult.TERMINATE; + addClassAndAllRef(allClassesName, foundClassName); + return FileVisitResult.CONTINUE; } @Override @@ -269,4 +355,34 @@ class StructureHelper { e.printStackTrace(); } } + + private void addClassAndAllRef(Set<String> allClassesName, String foundClassName) { + allClassesName.add(foundClassName); + try { + Class<?> foundClass = Class.forName(foundClassName); + List.of(foundClass.getInterfaces()).stream().map(Class::getCanonicalName).forEach(allClassesName::add); + List.of(foundClass.getDeclaredClasses()).stream().map(Class::getCanonicalName).forEach(allClassesName::add); + List.of(foundClass.getDeclaredFields()).stream().map(Field::getType) + .map(clazz -> clazz.isArray() ? clazz.componentType() : clazz) + .filter(Predicate.not(Class::isPrimitive)).map(Class::getCanonicalName) + .forEach(allClassesName::add); + List.of(foundClass.getDeclaredMethods()).stream().map(Method::getReturnType) + .map(clazz -> clazz.isArray() ? clazz.componentType() : clazz) + .filter(Predicate.not(Class::isPrimitive)).map(Class::getCanonicalName) + .forEach(allClassesName::add); + List.of(foundClass.getDeclaredMethods()).stream().flatMap(m -> List.of(m.getParameters()).stream()) + .map(Parameter::getType).map(clazz -> clazz.isArray() ? clazz.componentType() : clazz) + .filter(Predicate.not(Class::isPrimitive)).map(Class::getCanonicalName) + .forEach(allClassesName::add); + List.of(foundClass.getDeclaredMethods()).stream().flatMap(m -> List.of(m.getExceptionTypes()).stream()) + .map(clazz -> clazz.isArray() ? clazz.componentType() : clazz) + .filter(Predicate.not(Class::isPrimitive)).map(Class::getCanonicalName) + .forEach(allClassesName::add); + if (foundClass.getSuperclass() != null) { + allClassesName.add(foundClass.getSuperclass().getCanonicalName()); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } }