From 89b027d4753fd49366312b1f5194f1a97285ce5a Mon Sep 17 00:00:00 2001 From: jez04 <david.jezek@post.cz> Date: Fri, 8 Nov 2024 12:54:05 +0100 Subject: [PATCH] feat: :tada: solution --- src/main/java/lab/DeadListener.java | 6 + src/main/java/lab/DrawableSimulable.java | 1 + src/main/java/lab/GameController.java | 94 +++--- src/main/java/lab/Level.java | 53 +++- src/main/java/lab/Monster.java | 23 +- src/main/java/lab/MujListener.java | 12 + src/main/java/lab/Player.java | 2 +- src/main/java/lab/WorldEntity.java | 11 + .../structure/test/ClassStructureTest.java | 243 ++++++---------- .../jez04/structure/test/StructureHelper.java | 272 ++++++++++++++++++ 10 files changed, 500 insertions(+), 217 deletions(-) create mode 100644 src/main/java/lab/DeadListener.java create mode 100644 src/main/java/lab/MujListener.java create mode 100644 src/test/java/jez04/structure/test/StructureHelper.java diff --git a/src/main/java/lab/DeadListener.java b/src/main/java/lab/DeadListener.java new file mode 100644 index 0000000..1a18002 --- /dev/null +++ b/src/main/java/lab/DeadListener.java @@ -0,0 +1,6 @@ +package lab; + +@FunctionalInterface +public interface DeadListener { + void monsterDead(); +} diff --git a/src/main/java/lab/DrawableSimulable.java b/src/main/java/lab/DrawableSimulable.java index d5b5d45..f4facca 100644 --- a/src/main/java/lab/DrawableSimulable.java +++ b/src/main/java/lab/DrawableSimulable.java @@ -6,4 +6,5 @@ public interface DrawableSimulable { void draw(GraphicsContext gc); void simulate(double deltaT); + int getZIndex(); } diff --git a/src/main/java/lab/GameController.java b/src/main/java/lab/GameController.java index 595ea34..e14b490 100644 --- a/src/main/java/lab/GameController.java +++ b/src/main/java/lab/GameController.java @@ -16,52 +16,64 @@ public class GameController { @FXML private Slider angle; - - @FXML - private Canvas canvas; - - @FXML - private Slider speed; - - @FXML - private Label playerName; - - @FXML - void spawn(ActionEvent event) { - level.getPlayer().spawn(); - - } - - @FXML - void initialize() { - assert angle != null : "fx:id=\"angle\" was not injected: check your FXML file 'gameWindow.fxml'."; - assert canvas != null : "fx:id=\"canvas\" was not injected: check your FXML file 'gameWindow.fxml'."; - assert speed != null : "fx:id=\"speed\" was not injected: check your FXML file 'gameWindow.fxml'."; - angle.valueProperty().addListener(new ChangeListener<Number>() { - @Override - public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { - level.getPlayer().setAngle(newValue.doubleValue()); - } - }); - speed.valueProperty().addListener(new ChangeListener<Number>() { - @Override - public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { - level.getPlayer().setSpeed(newValue.doubleValue()); - } - }); - - } - - public void startGame(String name, int numberOfMonsters) { - playerName.setText(name); - level = new Level(canvas.getWidth(), canvas.getHeight(), numberOfMonsters); + + @FXML + private Canvas canvas; + + @FXML + private Slider speed; + + @FXML + private Label playerName; + private int deadCount; + + @FXML + void spawn(ActionEvent event) { +// level.getPlayer().spawn(); + Monster monster = new Monster(level); + monster.addDeadListener(new MujListener()); + monster.addDeadListener(new Dead()); + monster.addDeadListener(() -> System.out.println("another lambda dead")); + monster.addDeadListener(this::updateDeadLabel); + level.add(monster); + + } + + private void updateDeadLabel() { + deadCount++; + playerName.setText(String.format("Deads: %03d", deadCount)); + } + + @FXML + void initialize() { + assert angle != null : "fx:id=\"angle\" was not injected: check your FXML file 'gameWindow.fxml'."; + assert canvas != null : "fx:id=\"canvas\" was not injected: check your FXML file 'gameWindow.fxml'."; + assert speed != null : "fx:id=\"speed\" was not injected: check your FXML file 'gameWindow.fxml'."; + angle.valueProperty() + .addListener((observable, oldValue, newValue) -> level.getPlayer().setAngle(newValue.doubleValue())); + speed.valueProperty() + .addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { + level.getPlayer().setSpeed(newValue.doubleValue()); + }); + + } + + public void startGame(String name, int numberOfMonsters) { + playerName.setText(name); + level = new Level(canvas.getWidth(), canvas.getHeight(), numberOfMonsters); timer = new DrawingThread(canvas, level); timer.start(); - } - + } + public void stop() { timer.stop(); } + private class Dead implements DeadListener { + @Override + public void monsterDead() { + System.out.println("Named dead"); + } + } } diff --git a/src/main/java/lab/Level.java b/src/main/java/lab/Level.java index 07e610b..af3bee7 100644 --- a/src/main/java/lab/Level.java +++ b/src/main/java/lab/Level.java @@ -1,5 +1,9 @@ package lab; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + import javafx.geometry.Dimension2D; import javafx.geometry.Point2D; import javafx.scene.canvas.GraphicsContext; @@ -9,20 +13,22 @@ public class Level { private double width; private double height; - private DrawableSimulable[] entities; + private List<DrawableSimulable> entities; + private List<DrawableSimulable> entitiesToAdd = new ArrayList<>(); + private List<DrawableSimulable> entitiesToRemove = new ArrayList<>(); private Player player; - + public Level(double width, double height, int monsterCount) { this.width = width; this.height = height; player = new Player(this, new Point2D(20, 250), new Point2D(100, -20)); - entities = new DrawableSimulable[monsterCount+4]; - entities[0] = new NicerObstacle(this, new Point2D(20, 150)); - entities[1] = new Obstacle(this, new Point2D(300, 200), new Dimension2D(80, 40)); - entities[2] = new Obstacle(this); - entities[3] = player; - for (int i = 4; i < entities.length; i++) { - entities[i] = new Monster(this); + entities = new ArrayList<>(); + entities.add(new NicerObstacle(this, new Point2D(20, 150))); + entities.add(new Obstacle(this, new Point2D(300, 200), new Dimension2D(80, 40))); + entities.add(new Obstacle(this)); + entities.add(player); + for (int i = 0; i < monsterCount; i++) { + entities.add(new Monster(this)); } } @@ -38,10 +44,10 @@ public class Level { for (DrawableSimulable entity : entities) { entity.simulate(delay); } - for (int i = 0; i < entities.length; i++) { - if (entities[i] instanceof Collisionable c1) { - for (int j = i + 1; j < entities.length; j++) { - if (entities[j] instanceof Collisionable c2) { + for (int i = 0; i < entities.size(); i++) { + if (entities.get(i) instanceof Collisionable c1) { + for (int j = i + 1; j < entities.size(); j++) { + if (entities.get(j) instanceof Collisionable c2) { if (c1.intersect(c2.getBoundingBox())) { c1.hitBy(c2); c2.hitBy(c1); @@ -50,6 +56,17 @@ public class Level { } } } + entities.removeAll(entitiesToRemove); + entities.addAll(entitiesToAdd); + entitiesToAdd.clear(); + entitiesToRemove.clear(); + entities.sort(Comparator.comparing(DrawableSimulable::getZIndex)); +// entities.sort(new Comparator<DrawableSimulable>() { +// @Override +// public int compare(DrawableSimulable o1, DrawableSimulable o2) { +// return Integer.compare(o1.getZIndex(), o2.getZIndex()); +// } +// }); } public double getWidth() { @@ -63,5 +80,13 @@ public class Level { public Player getPlayer() { return player; } - + + public void add(DrawableSimulable entity) { + entitiesToAdd.add(entity); + } + + public void remove(DrawableSimulable entity) { + entitiesToRemove.add(entity); + } + } diff --git a/src/main/java/lab/Monster.java b/src/main/java/lab/Monster.java index 220473a..64b677c 100644 --- a/src/main/java/lab/Monster.java +++ b/src/main/java/lab/Monster.java @@ -1,7 +1,10 @@ package lab; +import java.util.ArrayList; +import java.util.List; import java.util.Random; +import javafx.geometry.Dimension2D; import javafx.geometry.Point2D; import javafx.geometry.Rectangle2D; import javafx.scene.canvas.GraphicsContext; @@ -13,9 +16,10 @@ public class Monster extends WorldEntity implements Collisionable { private Image image; private Point2D speed; + private List<DeadListener> deadListeners = new ArrayList<>(); public Monster(Level level) { - super(level, new Point2D(0, 0)); + super(level, new Point2D(0, 0), 100); image = new Image(getClass().getResourceAsStream("red-monster.gif")); position = new Point2D(level.getWidth() * 0.5 + RANDOM.nextDouble(level.getWidth() * 0.5 - image.getWidth()), RANDOM.nextDouble(level.getHeight())); @@ -56,9 +60,24 @@ public class Monster extends WorldEntity implements Collisionable { @Override public void hitBy(Collisionable another) { if (another instanceof Player) { - changeDirection(); + level.remove(this); + level.add(new Obstacle(level, getPosition(), new Dimension2D(60, 30))); + fireMonsterDead(); } } + public boolean addDeadListener(DeadListener e) { + return deadListeners.add(e); + } + + public boolean removeDeadListener(DeadListener o) { + return deadListeners.remove(o); + } + + public void fireMonsterDead() { + for (DeadListener deadListener : deadListeners) { + deadListener.monsterDead(); + } + } } diff --git a/src/main/java/lab/MujListener.java b/src/main/java/lab/MujListener.java new file mode 100644 index 0000000..e7ba9fe --- /dev/null +++ b/src/main/java/lab/MujListener.java @@ -0,0 +1,12 @@ +package lab; + +public class MujListener implements DeadListener { + + @Override + public void monsterDead() { + System.out.println("AAAAhh!!!!"); + + } + + +} diff --git a/src/main/java/lab/Player.java b/src/main/java/lab/Player.java index 7bbb070..93577d7 100644 --- a/src/main/java/lab/Player.java +++ b/src/main/java/lab/Player.java @@ -16,7 +16,7 @@ public class Player extends WorldEntity implements Collisionable { private double angle; public Player(Level level, Point2D position, Point2D speed) { - super(level, position); + super(level, position, 50); this.speed = speed; } diff --git a/src/main/java/lab/WorldEntity.java b/src/main/java/lab/WorldEntity.java index cdeebff..dd43f7c 100644 --- a/src/main/java/lab/WorldEntity.java +++ b/src/main/java/lab/WorldEntity.java @@ -7,12 +7,19 @@ public abstract class WorldEntity implements DrawableSimulable{ protected final Level level; protected Point2D position; + private int zIndex; public WorldEntity(Level level, Point2D position) { this.level = level; this.position = position; + zIndex = 0; } + public WorldEntity(Level level, Point2D position, int zIndex) { + this.level = level; + this.position = position; + this.zIndex = zIndex; + } @Override public final void draw(GraphicsContext gc) { gc.save(); @@ -26,5 +33,9 @@ public abstract class WorldEntity implements DrawableSimulable{ return position; } + public int getZIndex() { + return zIndex; + } + } diff --git a/src/test/java/jez04/structure/test/ClassStructureTest.java b/src/test/java/jez04/structure/test/ClassStructureTest.java index aaf98cc..03f1244 100644 --- a/src/test/java/jez04/structure/test/ClassStructureTest.java +++ b/src/test/java/jez04/structure/test/ClassStructureTest.java @@ -3,13 +3,23 @@ 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.Constructor; 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; @@ -24,197 +34,112 @@ import org.reflections.util.ConfigurationBuilder; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.geometry.Rectangle2D; -import javafx.scene.canvas.GraphicsContext; class ClassStructureTest { - private static final String drawableSimulableName = "DrawableSimulable"; - private static final String collisionableName = "Collisionable"; - - Set<String> allClasses = getNameOfAllClasses(); + StructureHelper helper = new StructureHelper(); @Test void gameControllerExistenceTest() { - classExist("GameController"); - Class<?> c = getClass("GameController"); - hasPropertyWithAnnotation(c, ".*", FXML.class); - hasMethodRegexp(c, ".*", void.class, ActionEvent.class); + helper.classExist("GameController"); } + @Test void gameControllerFxmlTest() { - classExist("GameController"); - Class<?> c = getClass("GameController"); - hasPropertyWithAnnotation(c, ".*", FXML.class); - } - @Test - void gameControllerActionEventTest() { - classExist("GameController"); - Class<?> c = getClass("GameController"); - hasMethodRegexp(c, ".*", void.class, ActionEvent.class); + helper.classExist("GameController"); + Class<?> c = helper.getClass("GameController"); + helper.hasPropertyWithAnnotation(c, ".*", FXML.class); } @Test - void cannonExistenceTest() { - classExist("Player"); - } - @Test - void cannonExistenceSetAngleTest() { - classExist("Player"); - Class<?> c = getClass("Player"); - hasMethod(c, "setAngle"); + void gameControllerActionMethodTest() { + helper.classExist("GameController"); + Class<?> c = helper.getClass("GameController"); + helper.hasMethodRegexp(c, ".*", void.class, ActionEvent.class); } - @Test - void cannonExistenceSetSpeedTest() { - classExist("Player"); - Class<?> c = getClass("Player"); - hasMethod(c, "setSpeed"); - } - - private void isInterface(Class<?> c) { - assertTrue(c.isInterface(), c.getName() + " have to be interface."); - } - - private void classExist(String name) { - assertTrue(allClasses.stream().anyMatch(c -> c.endsWith(name)), "Interface " + name + " not found"); + @Test + void gameControllerLambdasTest() { + helper.classExist("GameController"); + Class<?> c = helper.getClass("GameController"); + long lamdaCount = helper.countMethodRegexp(c, "lambda\\$.*"); + long innerClasscount = helper.countClassesRegexp(".*GameController\\$.*"); + assertTrue(lamdaCount + innerClasscount >= 3, + "At least 3 inner classes or lamdas required for GameController but only " + + (lamdaCount + innerClasscount) + " found."); } - private 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."); - } - try { - return Class.forName(className); - } catch (ClassNotFoundException e) { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (PrintStream ps = new PrintStream(baos, true)) { - e.printStackTrace(ps); - } catch (Exception e2) { - Assertions.fail(e2.getMessage()); - } - String stackTrace = baos.toString(); - Assertions.fail("Class " + name + " not found.\n" + stackTrace); - return null; - } + @Test + void deadListenerExistenceTest() { + helper.classExist("DeadListener"); } - private void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array) { - List<Field> fields = Arrays.asList(classDef.getDeclaredFields()); - assertTrue(fields.stream().anyMatch(f -> { - if (f.getName().matches(propertyNameRegexp)) { - if (array) { - return f.getType().isArray() && f.getType().getComponentType().equals(type); - } else { - return f.getType().equals(type); - } - } - return false; - }), "No field " + propertyNameRegexp + " of type " + type.getName() + " (is array " + array + ") in class " - + classDef.getName()); + @Test + void deadListenerEventMethodTest() { + helper.classExist("DeadListener"); + Class<?> c = helper.getClass("DeadListener"); + helper.hasMethod(c, "monsterDead"); } - private void hasPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation) { - List<Field> fields = Arrays.asList(classDef.getDeclaredFields()); - assertTrue( - fields.stream().filter(f -> f.getName().matches(propertyNameRegexp)) - .flatMap(f -> Arrays.asList( - f.getAnnotations()).stream()).map(a -> a.annotationType()).anyMatch(a -> - a.equals(annotation)), - "No field " + propertyNameRegexp + " with annotation " + annotation.getName() + " in class " - + classDef.getName()); + @Test + void sceneCollectionTest() { + helper.classExist("Level"); + Class<?> c = helper.getClass("Level"); + 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."); } - private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType) { - List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName); - assertTrue( - methods.stream().filter(m -> m.getName().contains(methodName)) - .anyMatch(m -> m.getReturnType().equals(returnType)), - "Method " + methodName + " not return " + returnType.getName()); + @Test + void sceneMethodAddTest() { + helper.classExist("Level"); + Class<?> c = helper.getClass("Level"); + helper.hasMethod(c, "add", void.class, helper.getClass("DrawableSimulable")); + ; } - private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) { - List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName); - assertTrue( - methods.stream().filter(m -> m.getName().contains(methodName)) - .filter(m -> m.getReturnType().equals(returnType)) - .anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))), - "Method " + methodName + " has no all parrams:" - + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); + @Test + void levelMethodRemoveTest() { + helper.classExist("Level"); + Class<?> c = helper.getClass("Level"); + helper.hasMethod(c, "remove", void.class, helper.getClass("DrawableSimulable")); + ; } - private 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))), - "Method " + methodNameRegexp + " has no all parrams:" - + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); - } - private void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName, - Class<?> returnType, Class<?>... params) { - List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName); - assertTrue( - methods.stream().filter(m -> m.getName().contains(methodName)) - .filter(m -> m.getReturnType().equals(returnType) - && (Modifier.isAbstract(m.getModifiers()) == abstractTag) - && (Modifier.isFinal(m.getModifiers()) == finalTag)) - .anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))), - "Method " + methodName + " has no all params:" - + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); + @Test + void monsterMethodAddTest() { + helper.classExist("Monster"); + Class<?> c = helper.getClass("Monster"); + helper.hasMethod(c, "addDeadListener"); } - private void hasImplements(Class<?> clazz, String... interfaceNames) { - List<Class<?>> interfaces = new ArrayList<>(); - Arrays.asList(interfaceNames).stream().map(name -> getClass(name)).forEach(c -> interfaces.add(c)); - assertTrue(Arrays.asList(clazz.getInterfaces()).containsAll(interfaces), "Class not implements all interfaces:" - + interfaces.stream().map(Class::getName).collect(Collectors.joining(", "))); + @Test + void monsterMethodRemoveTest() { + helper.classExist("Monster"); + Class<?> c = helper.getClass("Monster"); + Class<?> l = helper.getClass("DeadListener"); + helper.hasMethodRegexp(c, "remove.*", List.of(void.class, boolean.class), l); } - private void hasExtends(Class<?> clazz, String parentName) { - Class<?> parent = getClass(parentName); - assertTrue(clazz.getSuperclass().equals(parent), - "Class " + clazz.getName() + " not extends class " + parentName); + @Test + void monsterMethodFireTest() { + helper.classExist("Monster"); + Class<?> c = helper.getClass("Monster"); + assertTrue(helper.countMethodRegexp(c, "fire.*") > 0, "Method fire.* in LochNess not found."); } - private 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); + @Test + void zIndexMethodTest() { + helper.classExist("DrawableSimulable"); + Class<?> c = helper.getClass("DrawableSimulable"); + helper.hasMethodRegexp(c, "get[zZ][iI]ndex", int.class, new Class[0]); } - private Set<String> getNameOfAllClasses() { - List<String> initClassesName = Arrays.asList("lab.Routines", "lab.App", "lab.DrawingThread"); - for (String className : initClassesName) { - try { - Class.forName(className); - } catch (ClassNotFoundException e) { - 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.") - || p.getName().startsWith("sun.") || p.getName().startsWith("javax.") - || p.getName().startsWith("javassist")) { - continue; - } - 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); - return true; - }))); - } - return allClasses; + @Test + void zIndexFieldTest() { + helper.classExist("WorldEntity"); + Class<?> c = helper.getClass("WorldEntity"); + helper.hasProperty(c, "[zZ][iI]ndex", int.class, false); } } diff --git a/src/test/java/jez04/structure/test/StructureHelper.java b/src/test/java/jez04/structure/test/StructureHelper.java new file mode 100644 index 0000000..aa10c81 --- /dev/null +++ b/src/test/java/jez04/structure/test/StructureHelper.java @@ -0,0 +1,272 @@ +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.reflections.Configuration; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; +import org.reflections.util.ConfigurationBuilder; + +class StructureHelper { + + Set<String> allClasses = getNameOfAllClasses(); + + public void isInterface(Class<?> c) { + assertTrue(c.isInterface(), c.getName() + " have to be interface."); + } + + public void classExist(String name) { + assertTrue(allClasses.stream().anyMatch(c -> c.endsWith(name)), "Class/Interface " + name + " not found"); + } + + 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."); + } + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (PrintStream ps = new PrintStream(baos, true)) { + e.printStackTrace(ps); + } catch (Exception e2) { + Assertions.fail(e2.getMessage()); + } + String stackTrace = baos.toString(); + Assertions.fail("Class " + name + " not found.\n" + stackTrace); + return null; + } + } + + public void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array) { + List<Field> fields = Arrays.asList(classDef.getDeclaredFields()); + assertTrue(fields.stream().anyMatch(f -> { + if (f.getName().matches(propertyNameRegexp)) { + if (array) { + return f.getType().isArray() && f.getType().getComponentType().equals(type); + } else { + return f.getType().equals(type); + } + } + return false; + }), "No field " + propertyNameRegexp + " of type " + type.getName() + " (is array " + array + ") in class " + + classDef.getName()); + } + + public void hasPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation) { + List<Field> fields = Arrays.asList(classDef.getDeclaredFields()); + assertTrue( + fields.stream().filter(f -> f.getName().matches(propertyNameRegexp)) + .flatMap(f -> Arrays.asList(f.getAnnotations()).stream()).map(a -> a.annotationType()) + .anyMatch(a -> a.equals(annotation)), + "No field " + propertyNameRegexp + " with annotation " + annotation.getName() + " in class " + + classDef.getName()); + } + + public void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType) { + List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); + assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName); + assertTrue( + methods.stream().filter(m -> m.getName().contains(methodName)) + .anyMatch(m -> m.getReturnType().equals(returnType)), + "Method " + methodName + " not return " + returnType.getName()); + } + + public void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) { + List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); + assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName); + assertTrue( + methods.stream().filter(m -> m.getName().contains(methodName)) + .filter(m -> m.getReturnType().equals(returnType)) + .anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))), + "Method " + methodName + " has no all parrams:" + + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); + } + + 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 countClassesRegexp(String classNameRegexp) { + return getNameOfAllClasses().stream().filter(className -> className.matches(classNameRegexp)).count(); + } + + 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))), + "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, + 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(", "))); + } + + public void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName, + Class<?> returnType, Class<?>... params) { + List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); + assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName); + assertTrue( + methods.stream().filter(m -> m.getName().contains(methodName)) + .filter(m -> m.getReturnType().equals(returnType) + && (Modifier.isAbstract(m.getModifiers()) == abstractTag) + && (Modifier.isFinal(m.getModifiers()) == finalTag)) + .anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))), + "Method " + methodName + " has no all params:" + + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); + } + + public void hasImplements(Class<?> clazz, String... interfaceNames) { + List<Class<?>> interfaces = new ArrayList<>(); + Arrays.asList(interfaceNames).stream().map(name -> getClass(name)).forEach(c -> interfaces.add(c)); + assertTrue(Arrays.asList(clazz.getInterfaces()).containsAll(interfaces), "Class not implements all interfaces:" + + interfaces.stream().map(Class::getName).collect(Collectors.joining(", "))); + } + + public void hasExtends(Class<?> clazz, String parentName) { + Class<?> parent = getClass(parentName); + assertTrue(clazz.getSuperclass().equals(parent), + "Class " + clazz.getName() + " not extends class " + parentName); + } + + 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 Set<String> getNameOfAllClasses() { + List<String> initClassesName = new ArrayList<>(); + dynamicaliFoundSomeClass(initClassesName); + initClassesName.addAll(List.of("lab.Routines", "lab.App", "lab.DrawingThread")); + for (String className : initClassesName) { + try { + Class.forName(className); + break; + } catch (ClassNotFoundException e) { + 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.") + || p.getName().startsWith("sun.") || p.getName().startsWith("javax.") + || p.getName().startsWith("javassist")) { + continue; + } + 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); + return true; + }))); + } + for (String string : allClasses) { + System.out.println(string); + } + return allClasses; + } + + public void dynamicaliFoundSomeClass(List<String> initClassesName) { + URL myClassUrl = StructureHelper.class.getResource("ClassStructureTest.class"); + myClassUrl.getFile(); + try { + Path classRoot = Paths.get(myClassUrl.toURI()).getParent().getParent().getParent().getParent(); + if ("test-classes".equals(classRoot.getFileName().toString())) { + classRoot = classRoot.getParent().resolve("classes"); + } + 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())) { + return FileVisitResult.SKIP_SUBTREE; + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + System.out.println("VISIT: " + file); + if ("module-info.class".equals(file.getFileName().toString())) { + return FileVisitResult.CONTINUE; + } + if (!file.getFileName().toString().endsWith(".class")) { + 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; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + }); + } catch (URISyntaxException | IOException e) { + e.printStackTrace(); + } + } +} -- GitLab