diff --git a/pom.xml b/pom.xml index 8dd299954c7cd00346d4747029af4e5808880b3b..173602cce489762953bb386e0be015f4ced3fcad 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 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>vsb-cs-java1</groupId> - <artifactId>lab05v1</artifactId> + <artifactId>lab07v1</artifactId> <version>0.0.1-SNAPHOST</version> <packaging>jar</packaging> <properties> @@ -54,4 +54,16 @@ <scope>test</scope> </dependency> </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.13.0</version> + <configuration> + <failOnError>false</failOnError> + </configuration> + </plugin> + </plugins> + </build> </project> diff --git a/src/main/java/lab/App.java b/src/main/java/lab/App.java index 735a8b46f274cea82e92e2aa1445583b12e8eb00..9543b11402f10c5e733e14fb53c490df31336adf 100644 --- a/src/main/java/lab/App.java +++ b/src/main/java/lab/App.java @@ -15,6 +15,9 @@ import javafx.stage.WindowEvent; */ public class App extends Application { + static { + System.out.println("aaa"); + } private GameController gameController; public static void main(String[] args) { launch(args); diff --git a/src/main/java/lab/BulletAnimated.java b/src/main/java/lab/BulletAnimated.java index d74427ca60b113770dec07e4796e5cf4dc43836d..e0de23723c455e00b19138217348e1db2687c929 100644 --- a/src/main/java/lab/BulletAnimated.java +++ b/src/main/java/lab/BulletAnimated.java @@ -1,5 +1,8 @@ package lab; +import java.util.ArrayList; +import java.util.List; + import javafx.geometry.Point2D; import javafx.geometry.Rectangle2D; import javafx.scene.canvas.GraphicsContext; @@ -11,6 +14,8 @@ public class BulletAnimated extends Bullet { private final Point2D initVelocity; private Cannon cannon; private Image image = new Image(this.getClass().getResourceAsStream("fireball-transparent.gif")); + private List<HitListener> hitListeners = + new ArrayList<>(); public BulletAnimated(World world, Cannon cannon, Point2D position, Point2D velocity, Point2D acceleration) { super(world, position, velocity, acceleration); @@ -27,7 +32,8 @@ public class BulletAnimated extends Bullet { @Override public void hitBy(Collisionable another) { if (another instanceof Ufo) { - reload(); + world.remove(this); + fireUfoDestroyed(); } } @@ -35,5 +41,19 @@ public class BulletAnimated extends Bullet { position = cannon.getPosition(); velocity = new Point2D(0, 0); } + + public boolean addHitListener(HitListener e) { + return hitListeners.add(e); + } + + public boolean removeHitListener(HitListener o) { + return hitListeners.remove(o); + } + + private void fireUfoDestroyed() { + for (HitListener hitListener : hitListeners) { + hitListener.ufoDestroyed(); + } + } } \ No newline at end of file diff --git a/src/main/java/lab/GameController.java b/src/main/java/lab/GameController.java index 3ad72637660c92fecd268de5ee0d3c212a5e9d09..35211711b0769e81d4f543db4d9eb0d147efd3ce 100644 --- a/src/main/java/lab/GameController.java +++ b/src/main/java/lab/GameController.java @@ -1,11 +1,10 @@ package lab; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.geometry.Point2D; import javafx.scene.canvas.Canvas; +import javafx.scene.control.Label; import javafx.scene.control.Slider; public class GameController { @@ -21,18 +20,37 @@ public class GameController { private DrawingThread timer; + @FXML + private Label hits; + private int hitcount = 0; @FXML void fire(ActionEvent event) { double angle = timer.getWorld().getCannon().getAngle(); double angleRad = Math.toRadians(angle); double speedValue = speed.getValue(); - timer.getWorld().getBulletAnimated().reload(); Point2D velocity = new Point2D( Math.cos(angleRad)*speedValue, Math.sin(angleRad)*speedValue); - timer.getWorld().getBulletAnimated().setVelocity(velocity); + BulletAnimated bulletAnimated = new BulletAnimated( + timer.getWorld(), + timer.getWorld().getCannon(), + timer.getWorld().getCannon().getPosition(), + velocity, World.GRAVITY); + timer.getWorld().add(bulletAnimated); + bulletAnimated.addHitListener(this::increaseHits); + bulletAnimated.addHitListener( + () -> System.out.println("au!!!!")); } + private void updateHits() { + hits.setText(String.format("Hit count: %03d", hitcount)); + } + + private void increaseHits() { + hitcount++; + updateHits(); + } + @FXML void initialize() { assert angle != null : "fx:id=\"angle\" was not injected: check your FXML file 'gameWindow.fxml'."; @@ -40,12 +58,9 @@ public class GameController { assert speed != null : "fx:id=\"speed\" was not injected: check your FXML file 'gameWindow.fxml'."; timer = new DrawingThread(canvas); timer.start(); - angle.valueProperty().addListener(new ChangeListener<Number>() { - @Override - public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { - timer.getWorld().getCannon().setAngle(newValue.doubleValue()); - } - }); + angle.valueProperty().addListener( + (observable, oldValue, newValue) -> + timer.getWorld().getCannon().setAngle(newValue.doubleValue())); } public void stop() { diff --git a/src/main/java/lab/HitListener.java b/src/main/java/lab/HitListener.java new file mode 100644 index 0000000000000000000000000000000000000000..833d7e3e5af31946fafc4f7883eab795287ed765 --- /dev/null +++ b/src/main/java/lab/HitListener.java @@ -0,0 +1,6 @@ +package lab; + +@FunctionalInterface +public interface HitListener { + void ufoDestroyed(); +} diff --git a/src/main/java/lab/Ufo.java b/src/main/java/lab/Ufo.java index 78ea525119625e1f61f338e2811b29e1c7ee946d..c55bfd776d67b082e28ce5d4a79c34425fca5f5a 100644 --- a/src/main/java/lab/Ufo.java +++ b/src/main/java/lab/Ufo.java @@ -54,7 +54,7 @@ public class Ufo extends WorldEntity implements Collisionable { @Override public void hitBy(Collisionable another) { if(another instanceof BulletAnimated || another instanceof Bullet) { - changeDirection(); + world.remove(this); } } } diff --git a/src/main/java/lab/World.java b/src/main/java/lab/World.java index 1f7b4e53a758e0ba63d8a34ef21d822ccf72b401..d34bdf6cb2b6ca210b0e15b586322a1384ce528b 100644 --- a/src/main/java/lab/World.java +++ b/src/main/java/lab/World.java @@ -1,32 +1,40 @@ package lab; +import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import javafx.geometry.Point2D; import javafx.scene.canvas.GraphicsContext; public class World { + public static final Point2D GRAVITY = new Point2D(0, 9.81); private final double width; private final double height; - private DrawableSimulable[] entities; + private List<DrawableSimulable> entities; + private Collection<DrawableSimulable> entitiesToRemove = new LinkedList<DrawableSimulable>(); + private Collection<DrawableSimulable> entitiesToAdd = new LinkedList<DrawableSimulable>(); + private Cannon cannon; - private BulletAnimated bulletAnimated; +// private BulletAnimated bulletAnimated; public World(double width, double height) { this.width = width; this.height = height; - entities = new DrawableSimulable[8]; + entities = new ArrayList<>(); cannon = new Cannon(this, new Point2D(0, height - 20), -45); - bulletAnimated = new BulletAnimated(this, cannon, new Point2D(0, height), new Point2D(50, -80), - new Point2D(0, 9.81)); - entities[0] = cannon; - entities[1] = new Bullet(this, new Point2D(0, height), new Point2D(30, -30), new Point2D(0, 9.81)); - entities[2] = bulletAnimated; - for (int i = 3; i < entities.length; i++) { - entities[i] = new Ufo(this); +// bulletAnimated = new BulletAnimated(this, cannon, new Point2D(0, height), new Point2D(50, -80), +// GRAVITY); + entities.add(cannon); + entities.add(new Bullet(this, new Point2D(0, height), new Point2D(30, -30), new Point2D(0, 9.81))); +// entities.add(bulletAnimated); + for (int i = 0; i < 3; i++) { + entities.add(new Ufo(this)); } } @@ -44,10 +52,10 @@ public class World { for (DrawableSimulable entity : entities) { entity.simulate(deltaT); } - 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); @@ -56,11 +64,23 @@ public class World { } } } + entities.removeAll(entitiesToRemove); + entities.addAll(entitiesToAdd); + entitiesToAdd.clear(); + entitiesToRemove.clear(); } public double getWidth() { return width; } + + public void add(DrawableSimulable entity) { + entitiesToAdd.add(entity); + } + public void remove(DrawableSimulable entity) { + entitiesToRemove.add(entity); + + } public double getHeight() { return height; @@ -70,8 +90,8 @@ public class World { return cannon; } - public BulletAnimated getBulletAnimated() { - return bulletAnimated; - } - +// public BulletAnimated getBulletAnimated() { +// return bulletAnimated; +// } +// } \ No newline at end of file diff --git a/src/main/resources/lab/gameWindow.fxml b/src/main/resources/lab/gameWindow.fxml index b65e058f8949927a1d3b0baf0007e3373fa33316..c2b94f192a11c3e3bd9bc79f608fae84cc19f2ed 100644 --- a/src/main/resources/lab/gameWindow.fxml +++ b/src/main/resources/lab/gameWindow.fxml @@ -4,6 +4,7 @@ <?import javafx.scene.Cursor?> <?import javafx.scene.canvas.Canvas?> <?import javafx.scene.control.Button?> +<?import javafx.scene.control.Label?> <?import javafx.scene.control.Slider?> <?import javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.HBox?> @@ -33,10 +34,13 @@ </HBox> </bottom> <center> - <Canvas fx:id="canvas" height="287.0" width="582.0" BorderPane.alignment="CENTER"> + <Canvas fx:id="canvas" height="306.0" width="582.0" BorderPane.alignment="CENTER"> <BorderPane.margin> <Insets /> </BorderPane.margin> </Canvas> </center> + <top> + <Label fx:id="hits" text="Hit count: 0" textAlignment="CENTER" BorderPane.alignment="CENTER" /> + </top> </BorderPane> diff --git a/src/main/resources/lab/scenery.gif b/src/main/resources/lab/scenery.gif deleted file mode 100644 index d65c8d3d921f20d9b41709b90e4c79e3a6bd6232..0000000000000000000000000000000000000000 Binary files a/src/main/resources/lab/scenery.gif and /dev/null differ diff --git a/src/test/java/jez04/structure/test/ClassStructureTest.java b/src/test/java/jez04/structure/test/ClassStructureTest.java index c545ff810230339a9776aeb920ce4afd9fd75d8a..820053648bea210a51013245bfb28d70910cce16 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,191 +34,100 @@ 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("Cannon"); - } - @Test - void cannonExistenceSetAngleTest() { - classExist("Cannon"); - Class<?> c = getClass("Cannon"); - hasMethod(c, "setAngle"); - } - - - 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"); + void gameControllerActionMethodTest() { + helper.classExist("GameController"); + Class<?> c = helper.getClass("GameController"); + helper.hasMethodRegexp(c, ".*", void.class, ActionEvent.class); } - 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 gameControllerLambdasTest() { + 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."); } - 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 hitListenerExistenceTest() { + helper.classExist("HitListener"); } - 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 hitListenerEventMethodTest() { + helper.classExist("HitListener"); + Class<?> c = helper.getClass("HitListener"); + helper.hasMethod(c, "ufoDestroyed"); } - 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 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."); } - 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 worldMethodAddTest() { + helper.classExist("World"); + Class<?> c = helper.getClass("World"); + helper.hasMethod(c, "add", 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 worldMethodRemoveTest() { + helper.classExist("World"); + Class<?> c = helper.getClass("World"); + helper.hasMethod(c, "remove", void.class, helper.getClass("DrawableSimulable")); + ; } - 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 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); } - 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 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); } - 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 bulletAnimatedMethodFireTest() { + helper.classExist("BulletAnimated"); + Class<?> c = helper.getClass("BulletAnimated"); + assertTrue(helper.countMethodRegexp(c, "fire.*") > 0, "Method fire.* in LochNess not found."); } - 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; - } } 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 0000000000000000000000000000000000000000..aa10c81e0b4ccbc5d8bd405731e00501a30f8c27 --- /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(); + } + } +}