diff --git a/pom.xml b/pom.xml
index 98920e892967505a3e290f091fd2be62d19dffe8..f9a7995f4ef8768e3ca8bd0ebc0c3d7e349f677b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,8 +2,8 @@
 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	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>lab11v1</artifactId>
+	<groupId>vsb-cs-java2</groupId>
+	<artifactId>java2lab02v1</artifactId>
 	<version>0.0.1-SNAPHOST</version>
 	<packaging>jar</packaging>
 	<properties>
@@ -60,6 +60,12 @@
 			<version>0.10.2</version>
 			<scope>test</scope>
 		</dependency>
+		<dependency>
+			<groupId>org.hamcrest</groupId>
+			<artifactId>hamcrest</artifactId>
+			<version>3.0</version>
+			<scope>test</scope>
+		</dependency>
 	</dependencies>
 	<build>
 		<plugins>
diff --git a/src/main/java/lab/App.java b/src/main/java/lab/App.java
index 9543b11402f10c5e733e14fb53c490df31336adf..ec8e12686df1b3bcc2f16c95652a1f65f66100cc 100644
--- a/src/main/java/lab/App.java
+++ b/src/main/java/lab/App.java
@@ -15,9 +15,6 @@ 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);
@@ -32,8 +29,8 @@ public class App extends Application {
 			GameController gameController = gameLoader.getController();
 			Scene scene = new Scene(root);
 			primaryStage.setScene(scene);
-			primaryStage.resizableProperty().set(false);
-			primaryStage.setTitle("Java 1 - 1th laboratory");
+//			primaryStage.resizableProperty().set(false);
+			primaryStage.setTitle("Java 2 - 2nd laboratory");
 			primaryStage.show();
 			// Exit program when main window is closed
 			primaryStage.setOnCloseRequest(this::exitProgram);
diff --git a/src/main/java/lab/BulletAnimated.java b/src/main/java/lab/BulletAnimated.java
index e0de23723c455e00b19138217348e1db2687c929..8274916689626b372fe3724bafe0b8082c45cc01 100644
--- a/src/main/java/lab/BulletAnimated.java
+++ b/src/main/java/lab/BulletAnimated.java
@@ -13,7 +13,7 @@ public class BulletAnimated extends Bullet {
 	private static final double SIZE = 40;
 	private final Point2D initVelocity;
 	private Cannon cannon;
-	private Image image = new Image(this.getClass().getResourceAsStream("fireball-transparent.gif"));
+	private static Image image = new Image(BulletAnimated.class.getResourceAsStream("fireball-transparent.gif"));
 	private List<HitListener> hitListeners = 
 			new ArrayList<>();
 
diff --git a/src/main/java/lab/DataImporter.java b/src/main/java/lab/DataImporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..735c61e3b17bf5be0a5df661c7186b98d639d1e3
--- /dev/null
+++ b/src/main/java/lab/DataImporter.java
@@ -0,0 +1,69 @@
+package lab;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class DataImporter {
+	private static final Pattern personTextPattern = Pattern.compile("\\{[^\\{\\}]*address");
+	private static final Pattern firstNamePattern = Pattern.compile("\"firstname\":\"([\\p{IsAlphabetic}']+)\"");
+	private static final Pattern lastNamePattern = Pattern.compile("\"lastname\":\"([\\p{IsAlphabetic}']+)\"");
+	private static final Pattern datePattern = Pattern.compile("\"birthday\":\"(\\d{4}-\\d{2}-\\d{2})\"");
+	private static final DateTimeFormatter czechDate = DateTimeFormatter.ofPattern("dd. MM. YYYY");
+
+	public static void main(String[] args) {
+		importPlayers();
+	}
+
+	public static void importPlayers() {
+		List<Person> persons = parseData(downloadText()).stream().map(DataImporter::parsePerson).toList();
+		
+		for (Person person : persons) {
+			System.out.printf("%s %s (%s):  age=%d, 50th: %s , next in: %d%n"
+					, person.getFirstName(), person.getLastName()
+					, person.getDayOfBirth().format(czechDate)
+					, person.getAge()
+					, person.get50thBirthDay().format(czechDate)
+					, person.getDaysToBirthday());
+		}
+	}
+
+	public static String downloadText() {
+		try {
+			URL url = new URI("https://fakerapi.it/api/v2/persons?_quantity=20").toURL();
+			try (InputStream is = url.openStream()) {
+				BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
+				return bufferedReader.lines().collect(Collectors.joining("\n"));
+			}
+		} catch (URISyntaxException | IOException e) {
+			e.printStackTrace();
+			return "";
+		}
+	}
+
+	public static List<String> parseData(String data) {
+		Matcher matcher = personTextPattern.matcher(data);
+		return matcher.results().map(MatchResult::group).toList();
+	}
+
+	public static Person parsePerson(String personText) {
+		String firstName = firstNamePattern.matcher(personText).results().map(result -> result.group(1)).findAny()
+				.orElse("");
+		String lastName = lastNamePattern.matcher(personText).results().map(result -> result.group(1)).findAny()
+				.orElse("");
+		LocalDate date = datePattern.matcher(personText).results().map(result -> result.group(1)).map(text -> LocalDate.parse(text, DateTimeFormatter.ISO_DATE)).findAny()
+				.orElse(LocalDate.EPOCH);
+		return new Person(firstName, lastName, date);
+	}
+}
diff --git a/src/main/java/lab/Person.java b/src/main/java/lab/Person.java
new file mode 100644
index 0000000000000000000000000000000000000000..3694c1ef267d7f6d7a47f8ea6f59b2c4131b7747
--- /dev/null
+++ b/src/main/java/lab/Person.java
@@ -0,0 +1,57 @@
+package lab;
+
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.temporal.ChronoUnit;
+
+public class Person {
+
+	private String firstName;
+	private String lastName;
+	private LocalDate dayOfBirth;
+
+	public Person(String firstName, String lastName, LocalDate dayOfBirth) {
+		super();
+		this.firstName = firstName;
+		this.lastName = lastName;
+		this.dayOfBirth = dayOfBirth;
+	}
+
+	public String getLastName() {
+		return lastName;
+	}
+
+	public void setLastName(String lastName) {
+		this.lastName = lastName;
+	}
+
+	public String getFirstName() {
+		return firstName;
+	}
+
+	public LocalDate getDayOfBirth() {
+		return dayOfBirth;
+	}
+	
+	public int getAge() {
+		return Period.between(dayOfBirth, LocalDate.now()).getYears();
+	}
+	
+	public static void main(String[] args) {
+		Person p = new Person("", "", LocalDate.now().minusDays(3700));
+		System.out.println(p.getAge());
+		System.out.println(p.getDaysToBirthday());
+	}
+	public LocalDate get50thBirthDay() {
+		return dayOfBirth.plusYears(50);
+	}
+	
+	public long getDaysToBirthday() {
+		LocalDate nextBirthday = dayOfBirth.withYear(LocalDate.now().getYear());
+		if(nextBirthday.isBefore(LocalDate.now())){
+			nextBirthday = nextBirthday.plusYears(1);
+		}
+		return ChronoUnit.DAYS.between(LocalDate.now(), nextBirthday);
+	}
+}
diff --git a/src/main/resources/lab/gameWindow.fxml b/src/main/resources/lab/gameWindow.fxml
index 7fc092a6f5db50316afebcd6bbdb4bdcd87ba6c1..33be739d9e2e405729ea29470944c5ab1d8a1ee6 100644
--- a/src/main/resources/lab/gameWindow.fxml
+++ b/src/main/resources/lab/gameWindow.fxml
@@ -4,18 +4,18 @@
 <?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.control.TableColumn?>
 <?import javafx.scene.control.TableView?>
 <?import javafx.scene.layout.BorderPane?>
 <?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.StackPane?>
 <?import javafx.scene.layout.VBox?>
 <?import javafx.scene.text.Font?>
 
-<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="471.0" prefWidth="901.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="lab.GameController">
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="421.0" prefWidth="849.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="lab.GameController">
    <bottom>
-      <HBox alignment="TOP_CENTER" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+      <HBox alignment="TOP_CENTER" prefHeight="66.0" prefWidth="866.0" BorderPane.alignment="CENTER">
          <children>
             <Slider fx:id="angle" majorTickUnit="15.0" max="90.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minorTickCount="5" showTickLabels="true" showTickMarks="true" HBox.hgrow="ALWAYS" />
             <Button mnemonicParsing="false" onAction="#fire" style="-fx-background-color: RED;" text="Fire" textAlignment="CENTER">
@@ -36,18 +36,18 @@
          </children>
       </HBox>
    </bottom>
-   <center>
-      <Canvas fx:id="canvas" height="306.0" width="582.0" BorderPane.alignment="CENTER">
+   <left>
+      <StackPane style="-fx-border-image-width: 3px; -fx-border-style: SOLID;" 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>
-   <right>
-      <VBox maxHeight="1.7976931348623157E308" maxWidth="-Infinity" BorderPane.alignment="CENTER">
+         <children>
+            <Canvas fx:id="canvas" height="306.0" style="-fx-border-width: 3px; -fx-border-style: SOLID; -fx-border-color: RGB(100.0,0.0,0.0);" width="582.0" StackPane.alignment="TOP_LEFT" />
+         </children>
+      </StackPane>
+   </left>
+   <center>
+      <VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" BorderPane.alignment="CENTER">
          <children>
             <TableView fx:id="scores" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
               <columns>
@@ -63,5 +63,5 @@
             <Button fx:id="btnLoadFirstTen" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#btnLoadFirstTenAction" text="Load First 10 from DB" />
          </children>
       </VBox>
-   </right>
+   </center>
 </BorderPane>
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 b54aa04a2ab7894f6573cdf0fbfe972b9f0b3305..1e4cce56387e92b679cfeae928d83e7d9b4fcf11 100644
--- a/src/test/java/jez04/structure/test/ClassStructureTest.java
+++ b/src/test/java/jez04/structure/test/ClassStructureTest.java
@@ -1,101 +1,132 @@
 package jez04.structure.test;
 
-import static org.junit.jupiter.api.Assertions.assertTrue;
+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.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.List;
 
 import org.junit.jupiter.api.Test;
 
-import javafx.event.ActionEvent;
-import javafx.fxml.FXML;
-import javafx.scene.control.TableColumn;
-import javafx.scene.control.TableView;
-
 class ClassStructureTest {
 
-	StructureHelper helper = new StructureHelper();
+	StructureHelper helper = StructureHelper.getInstance();
 
 	@Test
-	void gameControllerExistenceTest() {
-		helper.classExist("GameController");
+	void dataImporterTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException {
+		assertThat("", new ClassExist(".*Data.*", false, true));
+		Class<?> c = helper.getClassRegexp(".*Data.*");
 	}
 
 	@Test
-	void gameControllerFxmlTest() {
-		helper.classExist("GameController");
-		Class<?> c = helper.getClass("GameController");
-		helper.hasPropertyWithAnnotation(c, ".*", FXML.class);
+	void dataImporterDownloadTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException {
+		assertThat("", new ClassExist(".*Data.*", false, true));
+		Class<?> d = helper.getClassRegexp(".*Data.*");
+		assertThat(d,  new HasMethod(".*down.*", String.class).useRegExp(true).caseSensitive(false));
+		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 gameControllerButtonActionMethodTest() {
-		helper.classExist("GameController");
-		Class<?> c = helper.getClass("GameController");
-		long count = helper.countMethodRegexp(c, ".*", void.class, ActionEvent.class);
-		assertTrue(count > 1, "Only " + count+ " method handling button click found. Expected more then 1");
+	void dataImporterParseAllTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException {
+		assertThat("", new ClassExist(".*Data.*", false, true));
+		Class<?> d = helper.getClassRegexp(".*Data.*");
+		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 gameControllerTableViewTest() {
-		helper.classExist("GameController");
-		Class<?> c = helper.getClass("GameController");
-		helper.hasProperty(c, ".*", TableView.class, false);
-	}
-	
-	@Test
-	void gameControllerTableColumnTest() {
-		helper.classExist("GameController");
-		Class<?> c = helper.getClass("GameController");
-		helper.hasProperty(c, ".*", TableColumn.class, false);
+	void personTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException {
+		assertThat("", new ClassExist("Person", false, false));
+		Class<?> p = helper.getClass("Person", false);
 	}
 
 	@Test
-	void dbConnectorTest() {
-		helper.classExistRegexp(".*[Dd][Bb][cC]on.*");
-	}
+	void dataImporterParsePersonTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException {
+		assertThat("", new ClassExist(".*Data.*", false, true));
+		Class<?> d = helper.getClassRegexp(".*Data.*");
 
-	@Test
-	void dbConnectorGetAllTest() {
-		helper.classExistRegexp(".*[Dd][Bb][cC]on.*");
-		Class<?> scoreClass = helper.getClassRegexp(".*[sS]core.*");
-		Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*");
-		helper.hasMethodRegexp(c, ".*[aA]ll.*", List.class);
-		helper.hasMethodRegexp(c, ".*[iI]nsert.*", void.class, scoreClass);
-	}
-	@Test
-	void dbConnectorInsertTest() {
-		helper.classExistRegexp(".*[Dd][Bb][cC]on.*");
-		Class<?> scoreClass = helper.getClassRegexp(".*[sS]core.*");
-		Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*");
-		helper.hasMethodRegexp(c, ".*[iI]nsert.*", void.class, scoreClass);
+		assertThat("", new ClassExist("Person", false, 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 dbConnectorContainsJdbcTest() throws URISyntaxException, IOException {
-		helper.classExistRegexp(".*[Dd][Bb][cC]on.*");
-		Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*");
-		String src = helper.getSourceCode(c);
-		assertTrue(src.contains("jdbc:"), "No usage of jdbc detect.");
+	void personAgeTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException {
+		assertThat("", new ClassExist(".*Data.*", false, true));
+		Class<?> d = helper.getClassRegexp(".*Data.*");
+
+		assertThat("", new ClassExist("Person", false, 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 dbConnectorContainsDriverManagerTest() throws URISyntaxException, IOException {
-		helper.classExistRegexp(".*[Dd][Bb][cC]on.*");
-		Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*");
-		String src = helper.getSourceCode(c);
-		assertTrue(src.contains("DriverManager"), "No usage of DriverManager detect.");
+	void person50birthdayTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException {
+		assertThat("", new ClassExist(".*Data.*", false, true));
+		Class<?> d = helper.getClassRegexp(".*Data.*");
+
+		assertThat("", new ClassExist("Person", false, 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 dbConnectorContainsSqlTest() throws URISyntaxException, IOException {
-		helper.classExistRegexp(".*[Dd][Bb][cC]on.*");
-		Class<?> c = helper.getClassRegexp(".*[Dd][Bb][cC]on.*");
-		String src = helper.getSourceCode(c).toLowerCase();
-		assertTrue(src.contains("create "), "No usage of SQL create.");
-		assertTrue(src.contains("select "), "No usage of SQL select.");
-		assertTrue(src.contains("insert "), "No usage of SQL table.");
-		assertTrue(src.contains(" from "), "No usage of SQL from.");
-		assertTrue(src.contains(" table"), "No usage of SQL table.");
+	void personNextBirthdayTest() throws URISyntaxException, IOException, IllegalAccessException, InvocationTargetException {
+		assertThat("", new ClassExist(".*Data.*", false, true));
+		Class<?> d = helper.getClassRegexp(".*Data.*");
+
+		assertThat("", new ClassExist("Person", false, 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.");
 	}
+
 }
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 f3aed5d82d5483fad6c3733ace46c8ce5c29e785..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,18 +41,49 @@ 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.");
 	}
 
 	public void classExist(String name) {
-		assertTrue(allClasses.stream().anyMatch(c -> c.endsWith(name)), "Class/Interface " + name + " not found");
+		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");
 	}
 
 	public void classExistRegexp(String name) {
-		assertTrue(allClasses.stream().anyMatch(c -> c.matches(name)), "Class/Interface " + name + " not found");
+		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");
 	}
 
 	public Class<?> getClassDirectly(String name) {
@@ -57,7 +91,13 @@ class StructureHelper {
 	}
 
 	public Class<?> getClassRegexp(String name) {
-		String className = allClasses.stream().filter(c -> c.matches(name)).findAny().orElse(null);
+		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);
 		if (className == null) {
 			Assertions.fail("Class " + name + " not found.");
 		}
@@ -65,7 +105,13 @@ class StructureHelper {
 	}
 
 	public Class<?> getClass(String name) {
-		String className = allClasses.stream().filter(c -> c.endsWith(name)).findAny().orElse(null);
+		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);
 		if (className == null) {
 			Assertions.fail("Class " + name + " not found.");
 		}
@@ -88,10 +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) {
+		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 (f.getName().matches(propertyNameRegexp)) {
+		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 {
@@ -99,14 +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) {
 		List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
 		assertTrue(
-				fields.stream().filter(f -> f.getName().matches(propertyNameRegexp))
+				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 "
@@ -114,19 +183,36 @@ 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 -> m.getName().contains(methodName))
+				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());
 	}
 
 	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) {
 		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))
+				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:"
@@ -134,8 +220,15 @@ class StructureHelper {
 	}
 
 	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) {
 		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
-		List<Method> foundMethods = methods.stream().filter(m -> m.getName().contains(methodName))
+		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()) {
@@ -148,8 +241,13 @@ 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 -> m.getName().matches(methodNameRegexp)).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 {
@@ -170,7 +268,12 @@ class StructureHelper {
 	}
 
 	public long countClassesRegexp(String classNameRegexp) {
-		return getNameOfAllClasses().stream().filter(className -> className.matches(classNameRegexp)).count();
+		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();
 	}
 
 	public void hasConstructor(Class<?> classDef, Class<?>... params) {
@@ -195,33 +298,103 @@ class StructureHelper {
 
 	public void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType,
 			Class<?>... params) {
+		hasMethodRegexp(interfaceDef, methodNameRegexp, true, returnType, 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 -> m.getName().matches(methodNameRegexp)),
+		assertTrue(
+				methods.stream()
+						.anyMatch(m -> caseSensitive ? m.getName().matches(methodNameRegexp)
+								: m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())),
 				"No method " + methodNameRegexp);
 		assertTrue(
-				methods.stream().filter(m -> m.getName().matches(methodNameRegexp))
+				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) {
 		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
-		assertTrue(methods.stream().anyMatch(m -> m.getName().matches(methodNameRegexp)),
+		assertTrue(
+				methods.stream()
+						.anyMatch(m -> caseSensitive ? m.getName().matches(methodNameRegexp)
+								: m.getName().toLowerCase().matches(methodNameRegexp.toLowerCase())),
 				"No method " + methodNameRegexp);
-		return methods.stream().filter(m -> m.getName().matches(methodNameRegexp))
+		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) {
 		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))
+				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))
@@ -230,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));
@@ -249,8 +426,16 @@ 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 -> m.getName().contains(methodName)), "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();
+		
+}