diff --git a/.gitignore b/.gitignore index e295b15b15444d4e6e163fb51a2edb17668c3383..e4a159670c17c260446ba6a3b5e10d6ae7adf2b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/logs/ /target/ .settings/ .project diff --git a/pom.xml b/pom.xml index 37f2c2858d4e2b265be88dcdd886625bb171c575..fb407c91e0cc374eb2345fc22527dc22bc34106a 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,22 @@ <maven.compiler.target>21</maven.compiler.target> </properties> <dependencies> + <!-- + https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core --> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>2.24.3</version> + </dependency> + + <!-- + https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api --> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>2.24.3</version> + </dependency> + <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> diff --git a/src/main/java/lab/Setup.java b/src/main/java/lab/Setup.java index ab0f461d3436c64bd18610f877ece8cd3e6f3a05..6716305b44ee09e7135304617f15a7bd1fe8f07c 100644 --- a/src/main/java/lab/Setup.java +++ b/src/main/java/lab/Setup.java @@ -8,15 +8,30 @@ public class Setup { private static Setup instance; private ScoreStorageInterface scoreStorageInterface = new DbConnector(); - private double lochnessMinXPopsition = 0.5; - private double lochnessMinYPopsition = 0.5; - private double lochnessMinSpeed = 50; - private double lochnessMaxSpeed = 150; - private int lochnessMultiplier = 1; - private double boatCollisionHeight = 0.25; - private double boatHitPulseX = 20; - private double boatHitPulseYMin = 50; - private double boatHitPulseYMax = 100; + private double lochnessMinXPopsition; + private double lochnessMinYPopsition; + private double lochnessMinSpeed; + private double lochnessMaxSpeed; + private int lochnessMultiplier; + private double boatCollisionHeight; + private double boatHitPulseX; + private double boatHitPulseYMin; + private double boatHitPulseYMax; + + private Setup(ScoreStorageInterface scoreStorageInterface, double lochnessMinXPopsition, + double lochnessMinYPopsition, double lochnessMinSpeed, double lochnessMaxSpeed, int lochnessMultiplier, + double boatCollisionHeight, double boatHitPulseX, double boatHitPulseYMin, double boatHitPulseYMax) { + this.scoreStorageInterface = scoreStorageInterface; + this.lochnessMinXPopsition = lochnessMinXPopsition; + this.lochnessMinYPopsition = lochnessMinYPopsition; + this.lochnessMinSpeed = lochnessMinSpeed; + this.lochnessMaxSpeed = lochnessMaxSpeed; + this.lochnessMultiplier = lochnessMultiplier; + this.boatCollisionHeight = boatCollisionHeight; + this.boatHitPulseX = boatHitPulseX; + this.boatHitPulseYMin = boatHitPulseYMin; + this.boatHitPulseYMax = boatHitPulseYMax; + } public static void configure(Setup setting) { instance = setting; @@ -66,4 +81,82 @@ public class Setup { return lochnessMaxSpeed; } + public static Builder builder() { + return new Builder(); + } + + public static Setup getInstanceForHardcoreGame() { + return builder().lochnessMaxSpeed(500).lochnessMinSpeed(200).lochnessMultiplier(10).lochnessMinYPopsition(0.1) + .build(); + } + + public static class Builder { + private ScoreStorageInterface scoreStorageInterface = new DbConnector(); + private double lochnessMinXPopsition = 0.5; + private double lochnessMinYPopsition = 0.5; + private double lochnessMinSpeed = 50; + private double lochnessMaxSpeed = 150; + private int lochnessMultiplier = 1; + private double boatCollisionHeight = 0.25; + private double boatHitPulseX = 20; + private double boatHitPulseYMin = 50; + private double boatHitPulseYMax = 100; + + public Builder scoreStorageInterface(ScoreStorageInterface scoreStorageInterface) { + this.scoreStorageInterface = scoreStorageInterface; + return this; + } + + public Builder lochnessMinXPopsition(double lochnessMinXPopsition) { + this.lochnessMinXPopsition = lochnessMinXPopsition; + return this; + } + + public Builder lochnessMinYPopsition(double lochnessMinYPopsition) { + this.lochnessMinYPopsition = lochnessMinYPopsition; + return this; + } + + public Builder lochnessMinSpeed(double lochnessMinSpeed) { + this.lochnessMinSpeed = lochnessMinSpeed; + return this; + } + + public Builder lochnessMaxSpeed(double lochnessMaxSpeed) { + this.lochnessMaxSpeed = lochnessMaxSpeed; + return this; + } + + public Builder lochnessMultiplier(int lochnessMultiplier) { + this.lochnessMultiplier = lochnessMultiplier; + return this; + } + + public Builder boatCollisionHeight(double boatCollisionHeight) { + this.boatCollisionHeight = boatCollisionHeight; + return this; + } + + public Builder boatHitPulseX(double boatHitPulseX) { + this.boatHitPulseX = boatHitPulseX; + return this; + } + + public Builder boatHitPulseYMin(double boatHitPulseYMin) { + this.boatHitPulseYMin = boatHitPulseYMin; + return this; + } + + public Builder boatHitPulseYMax(double boatHitPulseYMax) { + this.boatHitPulseYMax = boatHitPulseYMax; + return this; + } + + public Setup build() { + return new Setup(scoreStorageInterface, lochnessMinXPopsition, lochnessMinYPopsition, lochnessMinSpeed, + lochnessMaxSpeed, lochnessMultiplier, boatCollisionHeight, boatHitPulseX, boatHitPulseYMin, + boatHitPulseYMax); + } + } + } diff --git a/src/main/java/lab/data/Score.java b/src/main/java/lab/data/Score.java index 01dc746bf946fa4492b77a46a8f80fb85d50d655..a9993a9c51be53b7870842a1757836b776292340 100644 --- a/src/main/java/lab/data/Score.java +++ b/src/main/java/lab/data/Score.java @@ -1,5 +1,6 @@ package lab.data; +import java.util.Objects; import java.util.Random; public class Score { @@ -30,6 +31,23 @@ public class Score { this.points = points; } + @Override + public int hashCode() { + return Objects.hash(name, points); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Score other = (Score) obj; + return Objects.equals(name, other.name) && points == other.points; + } + @Override public String toString() { return "Score [name=" + name + ", points=" + points + "]"; diff --git a/src/main/java/lab/game/LochNess.java b/src/main/java/lab/game/LochNess.java index 1329d6a166def995707e7f1f32586e59bde80a90..5fc2f4d2d25f06cfe339cecec0807d7fc6eb94bc 100644 --- a/src/main/java/lab/game/LochNess.java +++ b/src/main/java/lab/game/LochNess.java @@ -4,6 +4,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import javafx.geometry.Dimension2D; import javafx.geometry.Point2D; import javafx.geometry.Rectangle2D; @@ -13,6 +16,8 @@ import lab.Setup; public class LochNess extends WorldEntity implements Collisionable { + private static Logger log = LogManager.getLogger(LochNess.class); + private static final Random RANDOM = new Random(); private Point2D speed; @@ -48,6 +53,7 @@ public class LochNess extends WorldEntity implements Collisionable { position = new Point2D(scene.getSize().getWidth(), position.getY()); speed = speed.multiply(-1); } + log.trace("LochNess position: {}", position); } @Override @@ -57,6 +63,7 @@ public class LochNess extends WorldEntity implements Collisionable { public void changeDirection() { speed = speed.multiply(-1); + log.debug("LochNess chaned direction."); } @Override @@ -66,6 +73,7 @@ public class LochNess extends WorldEntity implements Collisionable { @Override public void hitBy(Collisionable another) { + log.trace("LochNess hitted by {}.", another); if (another instanceof Boat) { scene.remove(this); scene.add(new Rock(scene, getPosition(), new Dimension2D(10, 10))); diff --git a/src/main/java/lab/gui/App.java b/src/main/java/lab/gui/App.java index 1e505dba721037d8c75ece583c9d2b1aa64fa4af..8f4d07db91121a6ed4a820c8337ba27e67b79a41 100644 --- a/src/main/java/lab/gui/App.java +++ b/src/main/java/lab/gui/App.java @@ -3,6 +3,9 @@ package lab.gui; import java.io.IOException; import java.net.URL; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; @@ -19,12 +22,14 @@ import lab.Setup; */ public class App extends Application { + private static Logger log = LogManager.getLogger(App.class); private GameController gameController; private Stage primaryStage; public static void main(String[] args) { - Setup.configure(new Setup()); + log.info("Application lauched"); + Setup.configure(Setup.getInstanceForHardcoreGame()); launch(args); } @@ -38,7 +43,7 @@ public class App extends Application { // Exit program when main window is closed primaryStage.setOnCloseRequest(this::exitProgram); } catch (Exception e) { - e.printStackTrace(); + log.error("Error during game play.", e); } } @@ -71,9 +76,11 @@ public class App extends Application { gameController.stop(); } super.stop(); + log.info("Gamne stoped"); } private void exitProgram(WindowEvent evt) { + log.info("Exiting game"); System.exit(0); } } \ No newline at end of file diff --git a/src/main/java/lab/gui/GameController.java b/src/main/java/lab/gui/GameController.java index 3c3ea447bbc2baf7ceb91d8be2050b47d11b3084..d1666cf9c2c855691e4ace4c43ef96e74cb408eb 100644 --- a/src/main/java/lab/gui/GameController.java +++ b/src/main/java/lab/gui/GameController.java @@ -2,6 +2,9 @@ package lab.gui; import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.ActionEvent; @@ -20,6 +23,8 @@ import lab.game.Scene; public class GameController { + private static Logger log = LogManager.getLogger(GameController.class); + private Scene scene; @FXML @@ -101,19 +106,19 @@ public class GameController { assert boatPosition != 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'."; - boatPosition.valueProperty().addListener(new ChangeListener<Number>() { + boatPosition.valueProperty().addListener(new ChangeListener<>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { scene.getBoat().setPosInPercentage(newValue.doubleValue()); } }); - speed.valueProperty().addListener(new ChangeListener<Number>() { + speed.valueProperty().addListener(new ChangeListener<>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { scene.getBackground().setSpeed(newValue.doubleValue()); } }); - + log.info("Screeen initialized."); } public void startGame(String name, int numberOfMonsters) { diff --git a/src/main/java/lab/gui/MainScreenController.java b/src/main/java/lab/gui/MainScreenController.java index f5ca94a43ca50b184946b320aa90c33db6085103..8ac4454f1c823e1a32850dffc33bee8d806ed1a9 100644 --- a/src/main/java/lab/gui/MainScreenController.java +++ b/src/main/java/lab/gui/MainScreenController.java @@ -3,6 +3,9 @@ package lab.gui; import java.io.IOException; import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Button; @@ -21,6 +24,8 @@ import lab.game.Difficult; */ public class MainScreenController { + private static Logger log = LogManager.getLogger(MainScreenController.class); + @FXML private Button btnGenerateScore; @@ -100,6 +105,7 @@ public class MainScreenController { pointsColumn.setCellValueFactory(new PropertyValueFactory<>("points")); initDB(); + log.info("Screeen initialized."); } private void initDB() { diff --git a/src/main/java/lab/storage/DbConnector.java b/src/main/java/lab/storage/DbConnector.java index 73cbefac399e59111cf39d0bb7271dc9423b5a1c..4c30cada3cba160d146286bb60b37d6e4d5f8928 100644 --- a/src/main/java/lab/storage/DbConnector.java +++ b/src/main/java/lab/storage/DbConnector.java @@ -9,11 +9,17 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import lab.data.Score; +import lab.gui.GameController; public class DbConnector implements ScoreStorageInterface { + private static Logger log = LogManager.getLogger(DbConnector.class); + private static final String JDBC_CONECTIN_STRING = "jdbc:h2:file:./scoreDB"; @Override diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 07daa1f118ce972c6e6075932e19752e07953324..fdf26b8382571ab72a45d6f419fd92329a145896 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,8 +1,12 @@ module lab02_module { - requires transitive javafx.controls; - requires javafx.fxml; - requires javafx.base; - requires java.sql; - opens lab.gui to javafx.fxml; - exports lab.gui to javafx.fxml,javafx.graphics; + requires transitive javafx.controls; + requires javafx.fxml; + requires javafx.base; + requires java.sql; + requires org.apache.logging.log4j; + + opens lab.gui to javafx.fxml; + opens lab.data to javafx.base; + + exports lab.gui to javafx.fxml, javafx.graphics; } diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000000000000000000000000000000000000..da032d36b62811a46f0edf9bd0e5d3aa4cb66e58 --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration xmlns="https://logging.apache.org/xml/ns" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=" + https://logging.apache.org/xml/ns + https://logging.apache.org/xml/ns/log4j-config-2.xsd"> + <Appenders> + <Console name="Console"> + <PatternLayout + pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> + </Console> + <RollingFile name="File" fileName="logs/app.log" + filePattern="logs/app.%d{yyyy-MM-dd}.%i.log.gz"> + <PatternLayout + pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> + <DefaultRolloverStrategy max="5"> + <Delete basePath="logs"> + <IfAccumulatedFileSize exceeds="2.2M" /> + </Delete> + </DefaultRolloverStrategy> + <Policies> + <OnStartupTriggeringPolicy /> + <SizeBasedTriggeringPolicy size="10M" /> + <TimeBasedTriggeringPolicy interval="1" /> + </Policies> + </RollingFile> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="Console" level="INFO"/> + <AppenderRef ref="File" /> + </Root> + <Logger name="lab.game" level="TRACE"> + </Logger> + </Loggers> +</Configuration> diff --git a/src/test/java/jez04/structure/test/AllOfContinue.java b/src/test/java/jez04/structure/test/AllOfContinue.java new file mode 100644 index 0000000000000000000000000000000000000000..8eb250be7bf4933fb898098380bfe3b87cec6ffe --- /dev/null +++ b/src/test/java/jez04/structure/test/AllOfContinue.java @@ -0,0 +1,39 @@ +package jez04.structure.test; + +import java.util.Arrays; +import java.util.List; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +public class AllOfContinue<T> extends BaseMatcher<T> { + + private final List<Matcher<? super T>> matchers; + + @SafeVarargs + public AllOfContinue(Matcher<? super T> ... matchers) { + this(Arrays.asList(matchers)); + } + + public AllOfContinue(List<Matcher<? super T>> matchers) { + this.matchers = matchers; + } + + @Override + public boolean matches(Object o) { + for (Matcher<? super T> matcher : matchers) { + if (!matcher.matches(o)) { +// matcher.describeMismatch(o, mismatch); + return false; + } + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendList("(", " " + "and" + " ", ")", matchers); + } + +} diff --git a/src/test/java/jez04/structure/test/ClassExist.java b/src/test/java/jez04/structure/test/ClassExist.java new file mode 100644 index 0000000000000000000000000000000000000000..7e1f9b78a7f8f5039333470143b91954e77ef9fc --- /dev/null +++ b/src/test/java/jez04/structure/test/ClassExist.java @@ -0,0 +1,44 @@ +package jez04.structure.test; + +import org.hamcrest.Description; + +public class ClassExist extends StructureMatcher<String> { + + private String className; + private boolean useRegExp; + private boolean caseSensitive; + + public ClassExist(String className) { + this(className, true, false); + } + + public ClassExist(String className, boolean caseSensitive, boolean useRegExp) { + this.className = className; + this.useRegExp = useRegExp; + this.caseSensitive = caseSensitive; + } + + @Override + public boolean matches(Object actual) { + if (useRegExp) { + return structureHelper.getAllClasses().stream().anyMatch( + c -> caseSensitive ? c.matches(className) : c.toLowerCase().matches(className.toLowerCase())); + } else { + return structureHelper.getAllClasses().stream().anyMatch( + c -> caseSensitive ? c.endsWith(className) : c.toLowerCase().endsWith(className.toLowerCase())); + } + + } + + @Override + public void describeTo(Description description) { + description.appendText(String.format("class/interface with name '%s' comparsion params(%s %s) exists", className, + caseSensitive ? "" : "no case sensitive", useRegExp ? "using regexp" : "")); + } + + @Override + public void describeMismatch(Object item, Description description) { + description.appendValueList("no class match from:\n ", "\n ", "", structureHelper.getAllClasses()); + } + +} diff --git a/src/test/java/jez04/structure/test/ClassStructureTest.java b/src/test/java/jez04/structure/test/ClassStructureTest.java index 6efa52cae44b0468da62712e44556ca3728d9b9d..7eb5fb71569deb6664f436b92e6c17a26bec57ce 100644 --- a/src/test/java/jez04/structure/test/ClassStructureTest.java +++ b/src/test/java/jez04/structure/test/ClassStructureTest.java @@ -22,116 +22,6 @@ import org.junit.jupiter.api.Test; class ClassStructureTest { - StructureHelper helper = new StructureHelper(); - - @Test - void dataImporterTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException { - helper.classExist("DataImporter", false); - Class<?> d = helper.getClass("DataImporter", false); - } - - @Test - void dataImporterDownloadTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException { - helper.classExist("DataImporter", false); - Class<?> d = helper.getClass("DataImporter", false); - String src = helper.getSourceCode(d); - helper.hasMethod(d, ".*down.*", false, String.class); - Method download = helper.getMethod(d, ".*down.*", false, String.class); - String downloadedText = (String) download.invoke(null); - assertThat(downloadedText, allOf( - endsWith("}]}"), - startsWith("{\"status\":\"OK\""), - stringContainsInOrder("firstname"), - stringContainsInOrder("lastname"), - stringContainsInOrder("birthday") - )); - - } - - @Test - void dataImporterParseAllTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException { - helper.classExist("DataImporter", false); - Class<?> d = helper.getClass("DataImporter", false); - String src = helper.getSourceCode(d); - helper.hasMethod(d, ".*parse.*", false, List.class, String.class); - Method parse = helper.getMethod(d, ".*parse.*", false, List.class, String.class); - List<String> texts = (List<String>) parse.invoke(null, "{\"id\":1,\"firstname\":\"Tatum\",\"lastname\":\"Block\",\"email\":\"lonnie.bergstrom@stoltenberg.com\",\"phone\":\"+12397191764\",\"birthday\":\"1946-11-06\",\"gender\":\"male\",\"address\":{"); - assertThat(texts, not(empty())); - } - - @Test - void personTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException { - helper.classExist("Person", false); - Class<?> p = helper.getClass("Person", false); - } - - @Test - void dataImporterParsePersonTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException { - helper.classExist("DataImporter", false); - Class<?> d = helper.getClass("DataImporter", false); - String src = helper.getSourceCode(d); - - helper.classExist("Person", false); - Class<?> p = helper.getClass("Person", false); - helper.hasMethod(d, ".*parse.*", false, p, String.class); - Method parsePerson = helper.getMethod(d, ".*parse.*", false, p, String.class); - Object person = parsePerson.invoke(null, "{\"id\":1,\"firstname\":\"Tatum\",\"lastname\":\"Block\",\"email\":\"lonnie.bergstrom@stoltenberg.com\",\"phone\":\"+12397191764\",\"birthday\":\"1946-11-06\",\"gender\":\"male\",\"address\":{"); - assertThat(person, notNullValue()); - } - - @Test - void personAgeTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException { - helper.classExist("DataImporter", false); - Class<?> d = helper.getClass("DataImporter", false); - - helper.classExist("Person", false); - Class<?> p = helper.getClass("Person", false); - helper.hasMethod(d, ".*parse.*", false, p, String.class); - Method parsePerson = helper.getMethod(d, ".*parse.*", false, p, String.class); - Object person = parsePerson.invoke(null, "{\"id\":1,\"firstname\":\"Tatum\",\"lastname\":\"Block\",\"email\":\"lonnie.bergstrom@stoltenberg.com\",\"phone\":\"+12397191764\",\"birthday\":\"1946-11-06\",\"gender\":\"male\",\"address\":{"); - assertThat(person, notNullValue()); - - Method age = helper.getMethod(p, ".*age.*", false, int.class); - int result = (int)age.invoke(person); - assertEquals(78, result , "Calculate wrong age."); - } - - @Test - void person50birthdayTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException { - helper.classExist("DataImporter", false); - Class<?> d = helper.getClass("DataImporter", false); - String src = helper.getSourceCode(d); - - helper.classExist("Person", false); - Class<?> p = helper.getClass("Person", false); - helper.hasMethod(d, ".*parse.*", false, p, String.class); - Method parsePerson = helper.getMethod(d, ".*parse.*", false, p, String.class); - Object person = parsePerson.invoke(null, "{\"id\":1,\"firstname\":\"Tatum\",\"lastname\":\"Block\",\"email\":\"lonnie.bergstrom@stoltenberg.com\",\"phone\":\"+12397191764\",\"birthday\":\"1946-11-06\",\"gender\":\"male\",\"address\":{"); - assertThat(person, notNullValue()); - - Method birth50 = helper.getMethod(p, ".*50.*", false, LocalDate.class); - LocalDate ldBirth50 = (LocalDate)birth50.invoke(person); - assertEquals(LocalDate.of(1996, 11, 06), ldBirth50 , "Calculate wrong 50th birthday."); - - } - - @Test - void personNextBirthdayTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException { - helper.classExist("DataImporter", false); - Class<?> d = helper.getClass("DataImporter", false); - String src = helper.getSourceCode(d); - - helper.classExist("Person", false); - Class<?> p = helper.getClass("Person", false); - helper.hasMethod(d, ".*parse.*", false, p, String.class); - Method parsePerson = helper.getMethod(d, ".*parse.*", false, p, String.class); - LocalDate bod =LocalDate.now().plusDays(338).minusYears(10); - Object person = parsePerson.invoke(null, "{\"id\":1,\"firstname\":\"Tatum\",\"lastname\":\"Block\",\"email\":\"lonnie.bergstrom@stoltenberg.com\",\"phone\":\"+12397191764\",\"birthday\":\""+ bod.format(DateTimeFormatter.ISO_DATE) +"\",\"gender\":\"male\",\"address\":{"); - assertThat(person, notNullValue()); - - Method daysM = helper.getMethod(p, ".*days.*", false, long.class); - long days= (long)daysM.invoke(person); - assertEquals(338, days , "Calculate wrong days to next birthday."); - } + StructureHelper helper = StructureHelper.getInstance(); } diff --git a/src/test/java/jez04/structure/test/ContainsInnerClasses.java b/src/test/java/jez04/structure/test/ContainsInnerClasses.java new file mode 100644 index 0000000000000000000000000000000000000000..5c7952997243af56384183f1ad7b9a784bd82cbb --- /dev/null +++ b/src/test/java/jez04/structure/test/ContainsInnerClasses.java @@ -0,0 +1,70 @@ +package jez04.structure.test; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.List; +import java.util.stream.Collectors; + +import org.hamcrest.Description; + +public class ContainsInnerClasses extends StructureMatcher<Class<?>> { + + private String methodNameRegexp; + private boolean caseSensitive = true; + private boolean useRegExp = false; + private int count = 1; + private List<Class<?>> params; + + public ContainsInnerClasses(String methodNameRegexp) { + this.methodNameRegexp = methodNameRegexp; + } + + public ContainsInnerClasses caseSensitive(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + return this; + } + + public ContainsInnerClasses count(int count) { + this.count = count; + return this; + } + + public ContainsInnerClasses useRegExp(boolean useRegExp) { + this.useRegExp = useRegExp; + return this; + } + + @Override + public boolean matches(Object actual) { + if (actual instanceof Class c) { + long lamdaCount = structureHelper.countMethodRegexp(c, "lambda\\$.*"); + long innerClassCount = structureHelper.countClassesRegexp(c.getCanonicalName()+"\\$.*"); + long methodRefCount = 0; + try { + methodRefCount = structureHelper.countMethodReference(c); + } catch (URISyntaxException | IOException e) { + System.out.println("Cannot count method references"); + e.printStackTrace(); + } + return lamdaCount + innerClassCount+methodRefCount >= count; + } + return false; + } + + @Override + public void describeTo(Description description) { + params.stream().map(Class::getName).collect(Collectors.joining(", ")); + description.appendText( + String.format("Class should have inner classses or lambdas name (regexp) of type %s %s %s with params types %s", + methodNameRegexp, caseSensitive ? "" : "ignore case", "")); + } + + @Override + public void describeMismatch(Object item, Description description) { + if (item instanceof Class c) { + description.appendValueList("no method match from:\n ", "\n ", "", c.getDeclaredMethods()); + } else { + description.appendText("mismatched item is not class type"); + } + } +} diff --git a/src/test/java/jez04/structure/test/HasMethod.java b/src/test/java/jez04/structure/test/HasMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..142fec8b77806805f8560a89d83a64d9fb788809 --- /dev/null +++ b/src/test/java/jez04/structure/test/HasMethod.java @@ -0,0 +1,95 @@ +package jez04.structure.test; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hamcrest.Description; + +public class HasMethod extends StructureMatcher<Class<?>> { + + private String methodNameRegexp; + private Class<?> returnType; + private boolean caseSensitive = true; + private boolean useRegExp = false; + private Boolean abstractTag = null; + private Boolean finalTag = null; + private int count = 1; + private List<Class<?>> params; + + public HasMethod(String methodNameRegexp, Class<?> returnType, Class<?>... params) { + this.methodNameRegexp = methodNameRegexp; + this.returnType = returnType; + this.params = List.of(params); + } + + public HasMethod caseSensitive(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + return this; + } + + public HasMethod abstractTag(Boolean abstractTag) { + this.abstractTag = abstractTag; + return this; + } + + public HasMethod finalTag(Boolean finalTag) { + this.finalTag = finalTag; + return this; + } + + public HasMethod count(int count) { + this.count = count; + return this; + } + + public HasMethod useRegExp(boolean useRegExp) { + this.useRegExp = useRegExp; + return this; + } + + @Override + public boolean matches(Object actual) { + if (actual instanceof Class c) { + List<Method> methods = Arrays.asList(c.getDeclaredMethods()); + Stream<Method> streamOfMethods; + if (useRegExp) { + streamOfMethods = methods.stream().filter(m -> caseSensitive ? m.getName().matches(methodNameRegexp) + : m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())); + + } else { + streamOfMethods = methods.stream().filter(m -> caseSensitive ? m.getName().endsWith(methodNameRegexp) + : m.getName().toLowerCase().endsWith(methodNameRegexp.toLowerCase())); + } + streamOfMethods = streamOfMethods + .filter(m -> returnType != null ? returnType.equals(m.getReturnType()) : true) + .filter(m -> finalTag != null ? Modifier.isAbstract(m.getModifiers()) == abstractTag.booleanValue() + : true) + .filter(m -> abstractTag != null ? Modifier.isFinal(m.getModifiers()) == finalTag.booleanValue() + : true); + long co = streamOfMethods.count(); + return co >= count; + } + return false; + } + + @Override + public void describeTo(Description description) { + params.stream().map(Class::getName).collect(Collectors.joining(", ")); + description.appendText( + String.format("Class should have method name (regexp) of type %s %s %s with params types %s", + returnType, methodNameRegexp, caseSensitive ? "" : "ignore case", "")); + } + + @Override + public void describeMismatch(Object item, Description description) { + if (item instanceof Class c) { + description.appendValueList("no method match from:\n ", "\n ", "", c.getDeclaredMethods()); + } else { + description.appendText("mismatched item is not class type"); + } + } +} diff --git a/src/test/java/jez04/structure/test/HasProperty.java b/src/test/java/jez04/structure/test/HasProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..0e7098dfaccbfef827f8abe1ea02c5bd09c5d855 --- /dev/null +++ b/src/test/java/jez04/structure/test/HasProperty.java @@ -0,0 +1,91 @@ +package jez04.structure.test; + +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.hamcrest.Description; + +public class HasProperty extends StructureMatcher<Class<?>> { + + private String propertyNameRegexp; + private Class<?> type; + private Predicate<Type> genericTypeFilter; + private Predicate<Class<?>> typeFilter; + private Boolean array = false; + private boolean caseSensitive; + private Class<?> annotation = null; + private int count = 1; + + public HasProperty(String propertyNameRegexp, Class<?> type, Boolean array) { + this(propertyNameRegexp, type, array, true); + } + + public HasProperty(String propertyNameRegexp, Class<?> type, Boolean array, boolean caseSensitive) { + this.propertyNameRegexp = propertyNameRegexp; + this.type = type; + this.array = array; + this.caseSensitive = caseSensitive; + } + + public HasProperty annotation(Class<?> annotation) { + this.annotation = annotation; + return this; + } + + public HasProperty typeFilter(Predicate<Class<?>> typeFilter) { + this.typeFilter = typeFilter; + return this; + } + + public HasProperty genericTypeFilter(Predicate<Type> genericTypeFilter) { + this.genericTypeFilter = genericTypeFilter; + return this; + } + + public HasProperty count(int count) { + this.count = count; + return this; + } + + @Override + public boolean matches(Object actual) { + if (actual instanceof Class c) { + Stream<?> streamOfResults; + List<Field> fields = Arrays.asList(c.getDeclaredFields()); + Stream<Field> streamOfFields = fields.stream() + .filter(f -> caseSensitive ? f.getName().matches(propertyNameRegexp) + : f.getName().toLowerCase().matches(propertyNameRegexp.toLowerCase())) + .filter(f -> type != null ? f.getType().equals(type) : true) + .filter(f -> array != null ? f.getType().isAnnotation() == array.booleanValue() : true) + .filter(f -> genericTypeFilter != null ? genericTypeFilter.test(f.getGenericType()) : true) + .filter(f -> typeFilter != null ? typeFilter.test(f.getType()) : true); + streamOfResults = streamOfFields; + if (annotation != null) { + streamOfResults = streamOfFields.flatMap(f -> Arrays.asList(f.getAnnotations()).stream()) + .map(a -> a.annotationType()).filter(a -> a.equals(annotation)); + } + long actualCount = streamOfResults.count(); + return this.count <= actualCount; + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText(String.format("Class should have field of type %s%s with name match regexp '%s'%s", type, + array != null && array ? "[]" : "", propertyNameRegexp, caseSensitive ? "" : "ignore case")); + } + + @Override + public void describeMismatch(Object item, Description description) { + if (item instanceof Class c) { + description.appendValueList("none of", ", ", "match", c.getDeclaredFields()); + } else { + description.appendText("mismatched item is not class type"); + } + } +} diff --git a/src/test/java/jez04/structure/test/IsDescendatOf.java b/src/test/java/jez04/structure/test/IsDescendatOf.java new file mode 100644 index 0000000000000000000000000000000000000000..a202faa8359dd596d8b8ab9fc8dac8426a66967f --- /dev/null +++ b/src/test/java/jez04/structure/test/IsDescendatOf.java @@ -0,0 +1,24 @@ +package jez04.structure.test; + +import org.hamcrest.Description; + +public class IsDescendatOf extends StructureMatcher<Class<?>> { + + private String className; + public IsDescendatOf(String className) { + this.className = className; + } + @Override + public boolean matches(Object actual) { + if(actual instanceof Class c) { + return structureHelper.getClass(className).isAssignableFrom(c); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText(String.format("cass shoud be descendant of %s", className)); + } + +} diff --git a/src/test/java/jez04/structure/test/IsInterface.java b/src/test/java/jez04/structure/test/IsInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..2abdee29266beb46899bf441eb75b6b8adef861c --- /dev/null +++ b/src/test/java/jez04/structure/test/IsInterface.java @@ -0,0 +1,23 @@ +package jez04.structure.test; + +import org.hamcrest.Description; + +public class IsInterface extends StructureMatcher<Class<?>> { + + public IsInterface() { + } + + @Override + public boolean matches(Object actual) { + if (actual instanceof Class c) { + return c.isInterface(); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText(String.format("value should be interface")); + } + +} diff --git a/src/test/java/jez04/structure/test/ResourceContains.java b/src/test/java/jez04/structure/test/ResourceContains.java new file mode 100644 index 0000000000000000000000000000000000000000..e7d92772860242e62e7a508e6813cf5d04de06cc --- /dev/null +++ b/src/test/java/jez04/structure/test/ResourceContains.java @@ -0,0 +1,102 @@ +package jez04.structure.test; + +import java.io.IOException; +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.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +import org.hamcrest.Description; + +public class ResourceContains extends StructureMatcher<String> { + private String regexp; + private boolean caseInsensitive; + private boolean srcFound; + private int count = 1; + + public ResourceContains(String regexp, boolean caseInsensitive) { + this.regexp = regexp; + this.caseInsensitive = caseInsensitive; + } + + public ResourceContains count(int count) { + this.count = count; + return this; + } + + @Override + public boolean matches(Object actual) { + srcFound = true; + List<Path> foundResources = new LinkedList<>(); + Pattern p; + if (caseInsensitive) { + p = Pattern.compile(regexp, Pattern.CASE_INSENSITIVE); + } else { + p = Pattern.compile(regexp); + } + try { + 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 resourcesRoot = classRoot.getParent().getParent().resolve(Paths.get("src", "main", "resources")); + System.out.println("resources root: " + resourcesRoot); + Files.walkFileTree(resourcesRoot, new FileVisitor<Path>() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (p.matcher(file.getFileName().toString()).matches()) { + foundResources.add(file); + } + return FileVisitResult.CONTINUE; + } + + @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; + } + }); + return foundResources.size() >= count; + } catch (URISyntaxException | IOException e) { + srcFound = false; + e.printStackTrace(); + return false; + } + } + + @Override + public void describeTo(Description description) { + description.appendText(String.format("Source code of class shoud contains regexp '%s'%s", regexp, + caseInsensitive ? " in case insensitive mode" : "")); + } + + @Override + public void describeMismatch(Object item, Description description) { + if (srcFound) { + description + .appendText(String.format("source code of class %s do not contains substring that match reg exp")); + } else { + description.appendText(String.format("source code of class %s was not found")); + } + } + +} diff --git a/src/test/java/jez04/structure/test/SrcContains.java b/src/test/java/jez04/structure/test/SrcContains.java new file mode 100644 index 0000000000000000000000000000000000000000..4ed7d6c3e0250f75646317233c2f39e74881ec75 --- /dev/null +++ b/src/test/java/jez04/structure/test/SrcContains.java @@ -0,0 +1,54 @@ +package jez04.structure.test; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.regex.Pattern; + +import org.hamcrest.Description; + +public class SrcContains extends StructureMatcher<Class<?>> { + private String regexp; + private boolean caseInsensitive; + private boolean srcFound; + + public SrcContains(String regexp, boolean caseInsensitive) { + this.regexp = regexp; + this.caseInsensitive = caseInsensitive; + } + + @Override + public boolean matches(Object actual) { + srcFound = true; + if (actual instanceof Class c) { + Pattern p = Pattern.compile(regexp); + if (caseInsensitive) { + p = Pattern.compile(regexp, Pattern.CASE_INSENSITIVE); + } + try { + return p.matcher(structureHelper.getSourceCode(c)).find(); + } catch (URISyntaxException | IOException e) { + srcFound = false; + e.printStackTrace(); + return false; + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText(String.format("Source code of class shoud contains regexp '%s'%s", regexp, + caseInsensitive ? " in case insensitive mode" : "")); + } + + @Override + public void describeMismatch(Object item, Description description) { + if (srcFound) { + description + .appendText(String.format("source code of class %s do not contains substring that match reg exp", item)); + } else { + description.appendText(String.format("source code of class %s was not found")); + } + } + +} diff --git a/src/test/java/jez04/structure/test/StructureHelper.java b/src/test/java/jez04/structure/test/StructureHelper.java index 6c18100e5074708094aab4e9a0a4099e43a13260..6ccabb4d3b2e307664acba8933c2ae35cfb83175 100644 --- a/src/test/java/jez04/structure/test/StructureHelper.java +++ b/src/test/java/jez04/structure/test/StructureHelper.java @@ -29,7 +29,10 @@ import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; import org.junit.jupiter.api.Assertions; import org.reflections.Configuration; import org.reflections.Reflections; @@ -38,8 +41,25 @@ import org.reflections.util.ConfigurationBuilder; class StructureHelper { + private static StructureHelper singeltonInstance; + + public static StructureHelper getInstance() { + if (singeltonInstance == null) { + singeltonInstance = new StructureHelper(); + } + return singeltonInstance; + } + Set<String> allClasses = getNameOfAllClasses(); + private StructureHelper() { + /* hide public one */ + } + + public Set<String> getAllClasses() { + return allClasses; + } + public void isInterface(Class<?> c) { assertTrue(c.isInterface(), c.getName() + " have to be interface."); } @@ -47,16 +67,23 @@ class StructureHelper { public void classExist(String name) { classExist(name, true); } + public void classExist(String name, boolean caseSensitive) { - assertTrue(allClasses.stream().anyMatch(c -> caseSensitive?c.endsWith(name):c.toLowerCase().endsWith(name.toLowerCase())), "Class/Interface " + name + " not found"); + assertTrue( + allClasses.stream() + .anyMatch(c -> caseSensitive ? c.endsWith(name) : c.toLowerCase().endsWith(name.toLowerCase())), + "Class/Interface " + name + " not found"); } public void classExistRegexp(String name) { classExistRegexp(name, true); } - + public void classExistRegexp(String name, boolean caseSensitive) { - assertTrue(allClasses.stream().anyMatch(c -> caseSensitive?c.matches(name):c.toLowerCase().matches(name.toLowerCase())), "Class/Interface " + name + " not found"); + assertTrue( + allClasses.stream() + .anyMatch(c -> caseSensitive ? c.matches(name) : c.toLowerCase().matches(name.toLowerCase())), + "Class/Interface " + name + " not found"); } public Class<?> getClassDirectly(String name) { @@ -66,23 +93,25 @@ class StructureHelper { public Class<?> getClassRegexp(String name) { return getClassRegexp(name, true); } - + public Class<?> getClassRegexp(String name, boolean caseSensitive) { - String className = allClasses.stream().filter(c -> - caseSensitive? - c.matches(name): - c.toLowerCase().matches(name.toLowerCase()) - ).findAny().orElse(null); + String className = allClasses.stream() + .filter(c -> caseSensitive ? c.matches(name) : c.toLowerCase().matches(name.toLowerCase())).findAny() + .orElse(null); if (className == null) { Assertions.fail("Class " + name + " not found."); } return loadClass(name, className); } + public Class<?> getClass(String name) { return getClass(name, true); } + public Class<?> getClass(String name, boolean caseSensitive) { - String className = allClasses.stream().filter(c -> caseSensitive?c.endsWith(name):c.toLowerCase().endsWith(name.toLowerCase())).findAny().orElse(null); + String className = allClasses.stream() + .filter(c -> caseSensitive ? c.endsWith(name) : c.toLowerCase().endsWith(name.toLowerCase())).findAny() + .orElse(null); if (className == null) { Assertions.fail("Class " + name + " not found."); } @@ -105,13 +134,27 @@ class StructureHelper { } } + public org.hamcrest.Matcher<Class<?>> hasProperty(String propertyNameRegexp, Class<?> type, boolean array) { + return new HasProperty(propertyNameRegexp, type, array); + } + public void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array) { hasProperty(classDef, propertyNameRegexp, type, array, true); } - public void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array, boolean caseSensitive) { + + public void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array, + boolean caseSensitive) { + assertTrue(hasPropertyB(classDef, propertyNameRegexp, type, array, caseSensitive), + "No field " + propertyNameRegexp + " of type " + type.getName() + " (is array " + array + ") in class " + + classDef.getName()); + } + + public boolean hasPropertyB(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array, + boolean caseSensitive) { List<Field> fields = Arrays.asList(classDef.getDeclaredFields()); - assertTrue(fields.stream().anyMatch(f -> { - if (caseSensitive?f.getName().matches(propertyNameRegexp):f.getName().toLowerCase().matches(propertyNameRegexp.toLowerCase())) { + return fields.stream().anyMatch(f -> { + if (caseSensitive ? f.getName().matches(propertyNameRegexp) + : f.getName().toLowerCase().matches(propertyNameRegexp.toLowerCase())) { if (array) { return f.getType().isArray() && f.getType().getComponentType().equals(type); } else { @@ -119,18 +162,20 @@ class StructureHelper { } } 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) { hasPropertyWithAnnotation(classDef, propertyNameRegexp, annotation, true); } - - public void hasPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation, boolean caseSensitive) { + + public void hasPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation, + boolean caseSensitive) { List<Field> fields = Arrays.asList(classDef.getDeclaredFields()); assertTrue( - fields.stream().filter(f -> caseSensitive?f.getName().matches(propertyNameRegexp):f.getName().toLowerCase().matches(propertyNameRegexp.toLowerCase())) + fields.stream() + .filter(f -> caseSensitive ? f.getName().matches(propertyNameRegexp) + : f.getName().toLowerCase().matches(propertyNameRegexp.toLowerCase())) .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 " @@ -140,11 +185,14 @@ class StructureHelper { public void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType) { hasMethod(interfaceDef, methodName, returnType, true); } + public void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, boolean caseSensitive) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName); assertTrue( - methods.stream().filter(m -> caseSensitive?m.getName().matches(methodName):m.getName().toLowerCase().matches(methodName.toLowerCase())) + methods.stream() + .filter(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase())) .anyMatch(m -> m.getReturnType().equals(returnType)), "Method " + methodName + " not return " + returnType.getName()); } @@ -152,25 +200,35 @@ class StructureHelper { public void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) { hasMethod(interfaceDef, methodName, true, returnType, params); } - - public void hasMethod(Class<?> interfaceDef, String methodName, boolean caseSensitive, Class<?> returnType, Class<?>... params) { + + public void hasMethod(Class<?> interfaceDef, String methodName, boolean caseSensitive, Class<?> returnType, + Class<?>... params) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - assertTrue(methods.stream().anyMatch(m -> caseSensitive?m.getName().matches(methodName):m.getName().toLowerCase().matches(methodName.toLowerCase())), "No method " + methodName); assertTrue( - methods.stream().filter(m -> caseSensitive?m.getName().matches(methodName):m.getName().toLowerCase().matches(methodName.toLowerCase())) + methods.stream() + .anyMatch(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase())), + "No method " + methodName); + assertTrue( + methods.stream() + .filter(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase())) .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 Method getMethod(Class<?> interfaceDef, String methodName,Class<?> returnType, Class<?>... params) { + public Method getMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) { return getMethod(interfaceDef, methodName, true, returnType, params); } - public Method getMethod(Class<?> interfaceDef, String methodName, boolean caseSensitive, Class<?> returnType, Class<?>... params) { + public Method getMethod(Class<?> interfaceDef, String methodName, boolean caseSensitive, Class<?> returnType, + Class<?>... params) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - List<Method> foundMethods = methods.stream().filter(m -> caseSensitive?m.getName().matches(methodName):m.getName().toLowerCase().matches(methodName.toLowerCase())) + List<Method> foundMethods = methods.stream() + .filter(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase())) .filter(m -> m.getReturnType().equals(returnType)) .filter(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))).toList(); if (foundMethods.isEmpty()) { @@ -185,10 +243,11 @@ class StructureHelper { public long countMethodRegexp(Class<?> interfaceDef, String methodNameRegexp) { return countMethodRegexp(interfaceDef, methodNameRegexp, true); } - + public long countMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, boolean caseSensitive) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - return methods.stream().filter(m -> caseSensitive?m.getName().matches(methodNameRegexp):m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())).count(); + return methods.stream().filter(m -> caseSensitive ? m.getName().matches(methodNameRegexp) + : m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())).count(); } public long countMethodReference(Class<?> interfaceDef) throws URISyntaxException, IOException { @@ -211,8 +270,10 @@ class StructureHelper { public long countClassesRegexp(String classNameRegexp) { return countClassesRegexp(classNameRegexp, true); } + public long countClassesRegexp(String classNameRegexp, boolean caseSensitive) { - return getNameOfAllClasses().stream().filter(className -> caseSensitive?className.matches(classNameRegexp):className.toLowerCase().matches(classNameRegexp.toLowerCase())).count(); + return getNameOfAllClasses().stream().filter(className -> caseSensitive ? className.matches(classNameRegexp) + : className.toLowerCase().matches(classNameRegexp.toLowerCase())).count(); } public void hasConstructor(Class<?> classDef, Class<?>... params) { @@ -234,51 +295,106 @@ class StructureHelper { } return foundConstructors.get(0); } - + public void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType, Class<?>... params) { - hasMethodRegexp(interfaceDef, methodNameRegexp, true, returnType, params); + hasMethodRegexp(interfaceDef, methodNameRegexp, true, returnType, params); } - - public void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, boolean caseSensitive, Class<?> returnType, - Class<?>... params) { + + public void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, boolean caseSensitive, + Class<?> returnType, Class<?>... params) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - assertTrue(methods.stream().anyMatch(m -> caseSensitive?m.getName().matches(methodNameRegexp):m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())), + assertTrue( + methods.stream() + .anyMatch(m -> caseSensitive ? m.getName().matches(methodNameRegexp) + : m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())), "No method " + methodNameRegexp); assertTrue( - methods.stream().filter(m -> caseSensitive?m.getName().matches(methodNameRegexp):m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())) + methods.stream() + .filter(m -> caseSensitive ? m.getName().matches(methodNameRegexp) + : m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())) .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 boolean hasMethodRegexpTest(Class<?> interfaceDef, String methodNameRegexp, boolean caseSensitive, + Class<?> returnType, Class<?>... params) { + return hasMethodRegexpTest(interfaceDef, methodNameRegexp, caseSensitive, returnType, List.of(params)); + } + + public boolean hasMethodRegexpTest(Class<?> interfaceDef, String methodNameRegexp, boolean caseSensitive, + Class<?> returnType, List<Class<?>> params) { + List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); + if (!methods.stream().anyMatch(m -> caseSensitive ? m.getName().matches(methodNameRegexp) + : m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase()))) { + return false; + } + return methods.stream() + .filter(m -> caseSensitive ? m.getName().matches(methodNameRegexp) + : m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())) + .filter(m -> m.getReturnType().equals(returnType)) + .anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(params)); + } + public long countMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType, Class<?>... params) { return countMethodRegexp(interfaceDef, methodNameRegexp, true, returnType, params); } - - public long countMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, boolean caseSensitive, Class<?> returnType, - Class<?>... params) { + + public long countMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, boolean caseSensitive, + Class<?> returnType, Class<?>... params) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - assertTrue(methods.stream().anyMatch(m -> caseSensitive?m.getName().matches(methodNameRegexp):m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())), + assertTrue( + methods.stream() + .anyMatch(m -> caseSensitive ? m.getName().matches(methodNameRegexp) + : m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())), "No method " + methodNameRegexp); - return methods.stream().filter(m -> caseSensitive?m.getName().matches(methodNameRegexp):m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())) + return methods.stream() + .filter(m -> caseSensitive ? m.getName().matches(methodNameRegexp) + : m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())) .filter(m -> m.getReturnType().equals(returnType)) .filter(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))).count(); } + public boolean hasMethodTest(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName, + boolean caseSensitive, Class<?> returnType, Class<?>... params) { + return hasMethodTest(interfaceDef, finalTag, abstractTag, methodName, caseSensitive, returnType, List.of(params)); + } + public boolean hasMethodTest(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName, + boolean caseSensitive, Class<?> returnType, List<Class<?>> params) { + List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); + if (!methods.stream().anyMatch(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase()))) { + return false; + } + return methods.stream() + .filter(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase())) + .filter(m -> m.getReturnType().equals(returnType) + && (Modifier.isAbstract(m.getModifiers()) == abstractTag) + && (Modifier.isFinal(m.getModifiers()) == finalTag)) + .anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(params)); + } + public void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName, Class<?> returnType, Class<?>... params) { hasMethod(interfaceDef, finalTag, abstractTag, methodName, true, returnType, params); } - - public void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName, boolean caseSensitive, - Class<?> returnType, Class<?>... params) { + + public void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName, + boolean caseSensitive, Class<?> returnType, Class<?>... params) { List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods()); - assertTrue(methods.stream().anyMatch(m -> caseSensitive?m.getName().matches(methodName):m.getName().toLowerCase().matches(methodName.toLowerCase())), "No method " + methodName); assertTrue( - methods.stream().filter(m -> caseSensitive?m.getName().matches(methodName):m.getName().toLowerCase().matches(methodName.toLowerCase())) + methods.stream() + .anyMatch(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase())), + "No method " + methodName); + assertTrue( + methods.stream() + .filter(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase())) .filter(m -> m.getReturnType().equals(returnType) && (Modifier.isAbstract(m.getModifiers()) == abstractTag) && (Modifier.isFinal(m.getModifiers()) == finalTag)) @@ -287,6 +403,10 @@ class StructureHelper { + Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", "))); } + public boolean isDescendatOf(Class<?> clazz, String interfaceName) { + return getClass(interfaceName).isAssignableFrom(clazz); + } + 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)); @@ -308,10 +428,14 @@ class StructureHelper { public void hasMethod(Class<?> interfaceDef, String methodName) { hasMethod(interfaceDef, methodName, true); } - + public void hasMethod(Class<?> interfaceDef, String methodName, boolean caseSensitive) { List<Method> methods = Arrays.asList(interfaceDef.getMethods()); - assertTrue(methods.stream().anyMatch(m -> caseSensitive?m.getName().matches(methodName):m.getName().toLowerCase().matches(methodName.toLowerCase())), "No method " + methodName); + assertTrue( + methods.stream() + .anyMatch(m -> caseSensitive ? m.getName().matches(methodName) + : m.getName().toLowerCase().matches(methodName.toLowerCase())), + "No method " + methodName); } public String getSourceCode(Class<?> clazz) throws URISyntaxException, IOException { diff --git a/src/test/java/jez04/structure/test/StructureMatcher.java b/src/test/java/jez04/structure/test/StructureMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..6b8bc25eb09606250e61ed18e2955a96c02692d1 --- /dev/null +++ b/src/test/java/jez04/structure/test/StructureMatcher.java @@ -0,0 +1,7 @@ +package jez04.structure.test; + +public abstract class StructureMatcher<T> extends org.hamcrest.BaseMatcher<T>{ + + protected StructureHelper structureHelper = StructureHelper.getInstance(); + +}