Skip to content
Snippets Groups Projects
Commit f9fe3fa0 authored by jez04's avatar jez04
Browse files

feat: :tada: solution

parent 7881901a
Branches
No related merge requests found
Pipeline #2577 canceled with stages
Showing
with 172 additions and 1324 deletions
...@@ -11,8 +11,32 @@ ...@@ -11,8 +11,32 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<lombok.version>1.18.36</lombok.version>
</properties> </properties>
<repositories>
<repository>
<id>vsb-education-release</id>
<url>https://artifactory.cs.vsb.cz/repository/education-releases/</url>
</repository>
<repository>
<id>vsb-education-snapshot</id>
<url>https://artifactory.cs.vsb.cz/repository/education-snapshot/</url>
</repository>
</repositories>
<dependencies> <dependencies>
<dependency>
<groupId>cz.vsb.fei</groupId>
<artifactId>kelvin-java-unittest-support</artifactId>
<version>[0.0.1-SNAPSHOT,)</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- <!--
https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core --> https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency> <dependency>
...@@ -102,7 +126,13 @@ ...@@ -102,7 +126,13 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version> <version>3.13.0</version>
<configuration> <configuration>
<failOnError>false</failOnError> <annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
......
...@@ -2,161 +2,46 @@ package lab; ...@@ -2,161 +2,46 @@ package lab;
import lab.storage.DbConnector; import lab.storage.DbConnector;
import lab.storage.ScoreStorageInterface; import lab.storage.ScoreStorageInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Builder.Default;
@Getter
@AllArgsConstructor
@lombok.Builder(toBuilder = true)
public class Setup { public class Setup {
@Getter
private static Setup instance; private static Setup instance;
@Default
private ScoreStorageInterface scoreStorageInterface = new DbConnector(); private ScoreStorageInterface scoreStorageInterface = new DbConnector();
private double lochnessMinXPopsition; @Default
private double lochnessMinYPopsition; private double lochnessMinXPopsition = 0.5;
private double lochnessMinSpeed; @Default
private double lochnessMaxSpeed; private double lochnessMinYPopsition = 0.5;
private int lochnessMultiplier; @Default
private double boatCollisionHeight; private double lochnessMinSpeed = 50;
private double boatHitPulseX; @Default
private double boatHitPulseYMin; private double lochnessMaxSpeed = 150;
private double boatHitPulseYMax; @Default
private int lochnessMultiplier = 1;
private Setup(ScoreStorageInterface scoreStorageInterface, double lochnessMinXPopsition, @Default
double lochnessMinYPopsition, double lochnessMinSpeed, double lochnessMaxSpeed, int lochnessMultiplier, private double boatCollisionHeight = 0.25;
double boatCollisionHeight, double boatHitPulseX, double boatHitPulseYMin, double boatHitPulseYMax) { @Default
this.scoreStorageInterface = scoreStorageInterface; private double boatHitPulseX = 20;
this.lochnessMinXPopsition = lochnessMinXPopsition; @Default
this.lochnessMinYPopsition = lochnessMinYPopsition; private double boatHitPulseYMin = 50;
this.lochnessMinSpeed = lochnessMinSpeed; @Default
this.lochnessMaxSpeed = lochnessMaxSpeed; private double boatHitPulseYMax = 100;
this.lochnessMultiplier = lochnessMultiplier;
this.boatCollisionHeight = boatCollisionHeight;
this.boatHitPulseX = boatHitPulseX;
this.boatHitPulseYMin = boatHitPulseYMin;
this.boatHitPulseYMax = boatHitPulseYMax;
}
public static void configure(Setup setting) { public static void configure(Setup setting) {
instance = setting; instance = setting;
} }
public static Setup getInstance() {
return instance;
}
public ScoreStorageInterface getScoreStorageInterface() {
return scoreStorageInterface;
}
public double getBoatCollisionHeight() {
return boatCollisionHeight;
}
public double getBoatHitPulseX() {
return boatHitPulseX;
}
public double getBoatHitPulseYMin() {
return boatHitPulseYMin;
}
public double getBoatHitPulseYMax() {
return boatHitPulseYMax;
}
public int getLochnessMultiplier() {
return lochnessMultiplier;
}
public double getLochnessMinXPopsition() {
return lochnessMinXPopsition;
}
public double getLochnessMinYPopsition() {
return lochnessMinYPopsition;
}
public double getLochnessMinSpeed() {
return lochnessMinSpeed;
}
public double getLochnessMaxSpeed() {
return lochnessMaxSpeed;
}
public static Builder builder() {
return new Builder();
}
public static Setup getInstanceForHardcoreGame() { public static Setup getInstanceForHardcoreGame() {
return builder().lochnessMaxSpeed(500).lochnessMinSpeed(200).lochnessMultiplier(10).lochnessMinYPopsition(0.1) return builder().lochnessMaxSpeed(500).lochnessMinSpeed(200).lochnessMultiplier(10).lochnessMinYPopsition(0.1)
.build(); .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);
}
}
} }
package lab.data; package lab.data;
import java.util.Objects;
import java.util.Random; import java.util.Random;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@AllArgsConstructor
@EqualsAndHashCode
@ToString
public class Score { public class Score {
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
...@@ -10,49 +20,6 @@ public class Score { ...@@ -10,49 +20,6 @@ public class Score {
private String name; private String name;
private int points; private int points;
public Score(String name, int points) {
this.name = name;
this.points = points;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPoints() {
return points;
}
public void setPoints(int points) {
this.points = points;
}
@Override
public 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 + "]";
}
public static Score generate() { public static Score generate() {
return new Score(getRandomNick(), RANDOM.nextInt(50, 300)); return new Score(getRandomNick(), RANDOM.nextInt(50, 300));
} }
......
package lab.game; package lab.game;
public interface DeadListener { public interface DeadListener {
void lochnessDead(); void lochnessDead(LochNess lochNess);
} }
...@@ -91,7 +91,7 @@ public class LochNess extends WorldEntity implements Collisionable { ...@@ -91,7 +91,7 @@ public class LochNess extends WorldEntity implements Collisionable {
private void fireLochNessDead() { private void fireLochNessDead() {
for (DeadListener deadListener : deadListeners) { for (DeadListener deadListener : deadListeners) {
deadListener.lochnessDead(); deadListener.lochnessDead(this);
} }
} }
} }
package lab.game; package lab.game;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import javafx.geometry.Dimension2D; import javafx.geometry.Dimension2D;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext; import javafx.scene.canvas.GraphicsContext;
import lab.Setup; import lab.Setup;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class Scene { public class Scene {
private Dimension2D size; private Dimension2D size;
private List<DrawableSimulable> sceneEntitites; private List<DrawableSimulable> sceneEntitites;
private List<DrawableSimulable> entititesToAdd = new ArrayList<>(); private List<DrawableSimulable> entititesToAdd = new ArrayList<>();
private List<DrawableSimulable> entititesToRemove = new ArrayList<>(); private List<DrawableSimulable> entititesToRemove = new ArrayList<>();
private List<LochNessDeadLog> lochNessDeadLogs = new LinkedList<>();
private Boat boat; private Boat boat;
private Rock rock; private Rock rock;
...@@ -30,10 +35,22 @@ public class Scene { ...@@ -30,10 +35,22 @@ public class Scene {
sceneEntitites.add(rock); sceneEntitites.add(rock);
sceneEntitites.add(boat); sceneEntitites.add(boat);
for (int i = 0; i < numberOfMonsters * Setup.getInstance().getLochnessMultiplier(); i++) { for (int i = 0; i < numberOfMonsters * Setup.getInstance().getLochnessMultiplier(); i++) {
sceneEntitites.add(new LochNess(this)); LochNess lochNess = new LochNess(this);
lochNess.addDeadListener(ln -> lochNessDeadLogs.add(new LochNessDeadLog(LocalDateTime.now(), ln.getPosition())));
sceneEntitites.add(lochNess);
} }
} }
record LochNessDeadLog(LocalDateTime time, Point2D position) {}
public void printDeadLog() {
for (LochNessDeadLog lochNessDeadLog : lochNessDeadLogs) {
log.info(lochNessDeadLog);
}
}
public Dimension2D getSize() { public Dimension2D getSize() {
return size; return size;
} }
......
...@@ -30,7 +30,7 @@ public class App extends Application { ...@@ -30,7 +30,7 @@ public class App extends Application {
public static void main(String[] args) { public static void main(String[] args) {
log.info("Application lauched"); log.info("Application lauched");
Setup.configure(Setup.getInstanceForHardcoreGame()); Setup.configure(Setup.getInstanceForHardcoreGame().toBuilder().boatHitPulseX(-20).build());
launch(args); launch(args);
} }
...@@ -81,6 +81,9 @@ public class App extends Application { ...@@ -81,6 +81,9 @@ public class App extends Application {
} }
private void exitProgram(WindowEvent evt) { private void exitProgram(WindowEvent evt) {
if(gameController != null) {
gameController.stop();
}
log.info("Exiting game"); log.info("Exiting game");
System.exit(0); System.exit(0);
} }
......
...@@ -64,13 +64,13 @@ public class GameController { ...@@ -64,13 +64,13 @@ public class GameController {
@FXML @FXML
void changePosition(ActionEvent event) { void changePosition(ActionEvent event) {
LochNess lochNess = new LochNess(scene); LochNess lochNess = new LochNess(scene);
lochNess.addDeadListener(() -> log.info("LochNess dead!")); lochNess.addDeadListener(ln -> log.info("LochNess dead!"));
lochNess.addDeadListener(this::addDead); lochNess.addDeadListener(this::addDead);
scene.add(lochNess); scene.add(lochNess);
} }
private void addDead() { private void addDead(LochNess lochNess) {
deadLochNessCounter++; deadLochNessCounter++;
updateDeadLabel(); updateDeadLabel();
} }
...@@ -131,6 +131,7 @@ public class GameController { ...@@ -131,6 +131,7 @@ public class GameController {
public void stop() { public void stop() {
timer.stop(); timer.stop();
scene.printDeadLog();
} }
} }
...@@ -4,7 +4,8 @@ module lab04_module { ...@@ -4,7 +4,8 @@ module lab04_module {
requires javafx.base; requires javafx.base;
requires java.sql; requires java.sql;
requires org.apache.logging.log4j; requires org.apache.logging.log4j;
requires static lombok;
opens lab.gui to javafx.fxml; opens lab.gui to javafx.fxml;
opens lab.data to javafx.base; opens lab.data to javafx.base;
......
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);
}
}
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());
}
}
package jez04.structure.test; package jez04.structure.test;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.time.LocalDate; import java.nio.file.Path;
import java.time.format.DateTimeFormatter; import java.util.Objects;
import java.util.List; import java.util.stream.Stream;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import cz.vsb.fei.kelvin.unittest.SrcContains;
import cz.vsb.fei.kelvin.unittest.StructureHelper;
import cz.vsb.fei.kelvin.unittest.TextFileContains;
import cz.vsb.fei.kelvin.unittest.XmlFileContains;
class ClassStructureTest { class ClassStructureTest {
StructureHelper helper = StructureHelper.getInstance(); StructureHelper helper = StructureHelper.getInstance(ClassStructureTest.class);
@Test
void lombokAsDependencyTest() throws URISyntaxException {
XmlFileContains xmlFileContains = new XmlFileContains("pom.xml", "/project/dependencies/dependency/artifactId[text() = 'lombok']");
Path root = TextFileContains.getProjectRoot(getClass());
assertThat(root, xmlFileContains);
}
@Test
void lombokAsAnnotationProcessorTest() throws URISyntaxException {
assertThat(TextFileContains.getProjectRoot(getClass()), new XmlFileContains("pom.xml", "/project/build/plugins/plugin/artifactId[text() = 'maven-compiler-plugin']"));
assertThat(TextFileContains.getProjectRoot(getClass()), new XmlFileContains("pom.xml", "/project/build/plugins/plugin/configuration/annotationProcessorPaths/path/artifactId[text() = 'lombok']"));
}
@Test
void moduleInfoTest() throws URISyntaxException {
assertThat(TextFileContains.getProjectRoot(getClass()), new TextFileContains("module-info.java", "lombok;"));
}
@CsvSource({
"Setup,@Getter,1",
"Setup,@.*Builder,1",
"Setup,@.*Default,3"
})
@ParameterizedTest(name = "useLombokTest in {1} annotation {2}")
void useLombokConfigTest(String className, String text, int count) throws URISyntaxException, ClassNotFoundException {
Class<?> config = helper.getClass(className);
assertThat(config, new SrcContains(text).count(count));
}
@CsvSource({
"Score,@Setter,1",
"Score,@Getter,1",
"Score,@AllArgsConstructor,1",
"Score,@EqualsAndHashCode,1",
"Score,@ToString,1"
})
@ParameterizedTest(name = "useLombokTest in {1} annotation {2}")
void useLombokOrDataTest(String className, String text, int count) throws URISyntaxException, ClassNotFoundException {
Class<?> config = helper.getClass(className);
assertThat(config, Matchers.anyOf(new SrcContains(text).count(count),
new SrcContains("@Data").count(1)));
}
@Test
void recordTest() throws URISyntaxException {
long recordCount = Stream.concat(
helper.getAllNonInnerClasses().stream(),
helper.getAllInnerClasses().stream())
.filter(className -> !className.startsWith("javafx")).map(className -> {
try {
return helper.getClassDirectly(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}).filter(Objects::nonNull).filter(clazz -> clazz.isRecord()).count();
assertThat(recordCount, Matchers.greaterThanOrEqualTo(1L));
}
} }
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");
}
}
}
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");
}
}
}
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");
}
}
}
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));
}
}
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"));
}
}
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"));
}
}
}
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"));
}
}
}
This diff is collapsed.
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment