[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\npatreon: bobocode\n"
  },
  {
    "path": ".github/workflows/maven.yml",
    "content": "# This workflow will build a Java project with Maven\n# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven\n\nname: Build completed branch with tests\n\non:\n  push:\n    branches: [ completed ]\n  pull_request:\n    branches: [ completed ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up JDK 17\n      uses: actions/setup-java@v1\n      with:\n        java-version: 17\n    - name: Build with Maven\n      run: mvn -B package --file pom.xml\n"
  },
  {
    "path": ".github/workflows/projectActions.yml",
    "content": "name: Auto Assign to Project(s)\n\non:\n  issues:\n    types: [opened, labeled]\n  pull_request:\n    types: [opened, labeled]\nenv:\n  GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }}\n\njobs:\n  assign_one_project:\n    runs-on: ubuntu-latest\n    name: Assign to One Project\n    steps:\n      - name: Assign NEW issues project 4\n        uses: srggrs/assign-one-project-github-action@1.2.1\n        if: github.event.action == 'opened' || contains(github.event.issue.labels.*.name, 'java-fundamentals')\n        with:\n          project: 'https://github.com/bobocode-projects/java-fundamentals-exercises/projects/4'\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n**/*.iml\n**/target\n"
  },
  {
    "path": ".mvn/wrapper/MavenWrapperDownloader.java",
    "content": "/*\n * Copyright 2007-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport java.net.*;\nimport java.io.*;\nimport java.nio.channels.*;\nimport java.util.Properties;\n\npublic class MavenWrapperDownloader {\n\n    private static final String WRAPPER_VERSION = \"0.5.6\";\n    /**\n     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.\n     */\n    private static final String DEFAULT_DOWNLOAD_URL = \"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/\"\n        + WRAPPER_VERSION + \"/maven-wrapper-\" + WRAPPER_VERSION + \".jar\";\n\n    /**\n     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to\n     * use instead of the default one.\n     */\n    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =\n            \".mvn/wrapper/maven-wrapper.properties\";\n\n    /**\n     * Path where the maven-wrapper.jar will be saved to.\n     */\n    private static final String MAVEN_WRAPPER_JAR_PATH =\n            \".mvn/wrapper/maven-wrapper.jar\";\n\n    /**\n     * Name of the property which should be used to override the default download url for the wrapper.\n     */\n    private static final String PROPERTY_NAME_WRAPPER_URL = \"wrapperUrl\";\n\n    public static void main(String args[]) {\n        System.out.println(\"- Downloader started\");\n        File baseDirectory = new File(args[0]);\n        System.out.println(\"- Using base directory: \" + baseDirectory.getAbsolutePath());\n\n        // If the maven-wrapper.properties exists, read it and check if it contains a custom\n        // wrapperUrl parameter.\n        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);\n        String url = DEFAULT_DOWNLOAD_URL;\n        if(mavenWrapperPropertyFile.exists()) {\n            FileInputStream mavenWrapperPropertyFileInputStream = null;\n            try {\n                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);\n                Properties mavenWrapperProperties = new Properties();\n                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);\n                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);\n            } catch (IOException e) {\n                System.out.println(\"- ERROR loading '\" + MAVEN_WRAPPER_PROPERTIES_PATH + \"'\");\n            } finally {\n                try {\n                    if(mavenWrapperPropertyFileInputStream != null) {\n                        mavenWrapperPropertyFileInputStream.close();\n                    }\n                } catch (IOException e) {\n                    // Ignore ...\n                }\n            }\n        }\n        System.out.println(\"- Downloading from: \" + url);\n\n        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);\n        if(!outputFile.getParentFile().exists()) {\n            if(!outputFile.getParentFile().mkdirs()) {\n                System.out.println(\n                        \"- ERROR creating output directory '\" + outputFile.getParentFile().getAbsolutePath() + \"'\");\n            }\n        }\n        System.out.println(\"- Downloading to: \" + outputFile.getAbsolutePath());\n        try {\n            downloadFileFromURL(url, outputFile);\n            System.out.println(\"Done\");\n            System.exit(0);\n        } catch (Throwable e) {\n            System.out.println(\"- Error downloading\");\n            e.printStackTrace();\n            System.exit(1);\n        }\n    }\n\n    private static void downloadFileFromURL(String urlString, File destination) throws Exception {\n        if (System.getenv(\"MVNW_USERNAME\") != null && System.getenv(\"MVNW_PASSWORD\") != null) {\n            String username = System.getenv(\"MVNW_USERNAME\");\n            char[] password = System.getenv(\"MVNW_PASSWORD\").toCharArray();\n            Authenticator.setDefault(new Authenticator() {\n                @Override\n                protected PasswordAuthentication getPasswordAuthentication() {\n                    return new PasswordAuthentication(username, password);\n                }\n            });\n        }\n        URL website = new URL(urlString);\n        ReadableByteChannel rbc;\n        rbc = Channels.newChannel(website.openStream());\n        FileOutputStream fos = new FileOutputStream(destination);\n        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);\n        fos.close();\n        rbc.close();\n    }\n\n}\n"
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "content": "distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip\nwrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar\n"
  },
  {
    "path": "0-0-intro/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Introduction\n\nLearn how to use this course to build strong skill needed for enterprise Java development \n\n## The only way to learn effectively is to **learn by doing!** 💪\n\nTherefore, this whole repository consists of **various exercises grouped by topics**. By doing exercises you will **build strong skills and neven get stuck.** Introduction itself is an exercise, so you can understand the idea on the simplest example.\n\nEach exercise has three major parts:\n* **a task** – some logic that you implement in order build a certain skill 👨🏻‍💻\n* **a test** – a corresponding test that verifies if you implement the task correctly ▶️\n* **a completed solution** - a branch `completed` that holds implemented task ✅\n\nGo ahead and do this exercise by implementing a method in `Introduction` class. 💪 \n_(we know it's silly, but we wanted to give you a simple example 😀)_\n\n## You should have installed on your local machine ❗️\n* [JDK 11+](https://jdk.java.net/15/)\n* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)\n\n## How to start ❓\n* [clone](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) this [repository](https://github.com/bobocode-projects/java-fundamentals-exercises) to your computer\n* **open** the project via IDE\n\n## Have questions? 🧐\n* take a look at **README** of the exercise\n* switch to branch `completed` and **see the correct implementation** of the exercise\n* [join the discussion](https://github.com/bobocode-projects/java-fundamentals-exercises/discussions) on the GitHub\n* contact us via info@bobocode.com\n"
  },
  {
    "path": "0-0-intro/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>com.bobocode</groupId>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <artifactId>0-0-intro</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>java-fundamentals-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "0-0-intro/src/main/java/com/bobocode/intro/ExerciseIntroduction.java",
    "content": "package com.bobocode.intro;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * Welcome! This is an introduction exercise that will show you a simple example of Bobocode exercises.\n * <p>\n * JavaDoc is a way of communication with other devs. We use Java Docs in every exercise to define the task.\n * So PLEASE MAKE SURE you read the Java Docs carefully.\n * <p>\n * Every exercise is covered with tests, see {@link ExerciseIntroductionTest}.\n * <p>\n * In this repo you'll find dozens of exercises covering various fundamental topics.\n * They all have the same structure helping you to focus on practice and build strong skills!\n *\n * @author Taras Boychuk\n */\npublic class ExerciseIntroduction {\n    /**\n     * This method returns a very important message. If understood well, it can save you years of inefficient learning,\n     * and unlock your potential!\n     *\n     * @return \"The key to efficient learning is practice!\"\n     */\n    public String getWelcomeMessage() {\n        // todo: implement a method and return a message according to javadoc\n        throw new ExerciseNotCompletedException(); \n    }\n\n    /**\n     * Method encodeMessage accepts one {@link String} parameter and returns encoded {@link String}.\n     * <p>\n     * PLEASE NOTE THAT YOU WILL GET STUCK ON THIS METHOD INTENTIONALLY! ;)\n     * <p>\n     * Every exercise has a completed solution that is stored in the branch \"completed\". So in case you got stuck\n     * and don't know what to do, go check out completed solution.\n     *\n     * @param message input message\n     * @return encoded message\n     */\n    public String encodeMessage(String message) {\n        // todo: switch to branch \"completed\" in order to see how it should be implemented\n        throw new ExerciseNotCompletedException();\n    }\n}\n"
  },
  {
    "path": "0-0-intro/src/test/java/com/bobocode/intro/ExerciseIntroductionTest.java",
    "content": "package com.bobocode.intro;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.*;\n\nimport java.util.Arrays;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * This is a {@link ExerciseIntroductionTest} that is meant to verify if you properly implement {@link ExerciseIntroduction}. \n * It is a simple example that shows how each exercise is organized: todo section + tests.\n * <p>\n * A typical Java test uses JUnit framework to run the test, and may also use some other frameworks for assertions.\n * In our exercises we use JUnit 5 + AssertJ\n * <p>\n * PLEASE NOTE:\n * - annotation @{@link Order} is used to help you to understand which method should be implemented first.\n * - annotation @{@link DisplayName} is used to provide you more detailed instructions.\n *\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\nclass ExerciseIntroductionTest {\n    private ExerciseIntroduction exerciseIntroduction = new ExerciseIntroduction();\n    private String EXPECTED_MESSAGE = \"The key to efficient learning is practice!\";\n\n    @Test\n    @Order(1)\n    @DisplayName(\"getWelcomeMessage method returns correct phrase\")\n    void getWelcomeMessage() {\n        String message = exerciseIntroduction.getWelcomeMessage();\n\n        assertThat(message).isEqualTo(EXPECTED_MESSAGE);\n    }\n\n    @Test\n    @Order(2)\n    @DisplayName(\"encodeMessage returns correct encoded message\")\n    @SneakyThrows\n    void encodeMessageReturnsCorrectPhrase() {\n        var encodeMessageMethod = Arrays.stream(ExerciseIntroduction.class.getDeclaredMethods())\n                .filter(method -> method.getName().equals(\"encodeMessage\"))\n                .findAny()\n                .orElseThrow();\n\n        var encodedMessage = encodeMessageMethod.invoke(new ExerciseIntroduction(), EXPECTED_MESSAGE);\n\n        assertThat(encodedMessage).isEqualTo(\"VGhlIGtleSB0byBlZmZpY2llbnQgbGVhcm5pbmcgaXMgcHJhY3RpY2Uh\");\n    }\n}\n"
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Hello Generics :muscle:\n\nLearn how to do **safe type parametrization** using generic types 💪\n\n### Pre-conditions ❗\n\nYou're supposed to be familiar **Java classes** and **casting**\n\n### Objectives\n\n* refactor class `Box` to introduce a generic type ✅\n* change the value field type to parametrized (generic) type ✅\n* change the **constructor parameter type** to parametrized type ✅\n* change the **setter parameter type** to parametrized type ✅\n* change the **getter return type** to parametrized type ✅\n* refactor `BoxDemoApp` to specify generic type as `Integer` and receive **compile time error** ✅\n\n##\n\nIn many cases the logic we implement does not depend on the data type. In other words, **many algorithms are the same\nregardless if you apply them to Integer, String or any other types.** So in order to leverage the same logic for different \ntypes we need some form of type parametrization. Of course, we can use `Object` and store anything we want, but\nit is not safe. Using `Object` requires runtime casting which can cause runtime exceptions.\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>\n"
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>1-0-java-basics</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>1-3-0-hello-generics</artifactId>\n\n</project>"
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/Box.java",
    "content": "package com.bobocode.basics;\n\n/**\n * {@link Box} is a container class that can store a value of any given type. Using Object as a field type\n * is flexible, because we can store anything we want there. But it is not safe, because it requires runtime casting\n * and there is no guarantee that we know the type of the stored value.\n * <p>\n * todo: refactor this class so it uses generic type \"T\" and run {@link com.bobocode.basics.BoxTest} to verify it\n */\npublic class Box {\n    private Object value;\n\n    public Box(Object value) {\n        this.value = value;\n    }\n\n    public Object getValue() {\n        return value;\n    }\n\n    public void setValue(Object value) {\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/BoxDemoApp.java",
    "content": "package com.bobocode.basics;\n\n/**\n * This demo demonstrates why using Object is not safe. It's not safe because runtime casting can cause runtime\n * exceptions. We should always fail as soon as possible. So in this code we should not allow setting String\n * value at compile time, if we expect to work with integers.\n * <p>\n * todo: refactor class {@link Box} to make type parameterization safe and make this demo fail at compile time\n */\npublic class BoxDemoApp {\n    public static void main(String[] args) {\n        Box intBox = new Box(123);\n        Box intBox2 = new Box(321);\n        System.out.println((int) intBox.getValue() + (int) intBox2.getValue());\n\n        intBox.setValue(222);\n        intBox.setValue(\"abc\"); // this should not be allowed\n        // the following code will compile, but will throw runtime exception\n        System.out.println((int) intBox.getValue() + (int) intBox2.getValue());\n    }\n}\n"
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/src/test/java/com/bobocode/basics/BoxTest.java",
    "content": "package com.bobocode.basics;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.*;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class BoxTest {\n    private final String TYPE_PARAMETER_NAME = \"T\";\n\n    @Test\n    @Order(1)\n    @DisplayName(\"Box class has one type parameter\")\n    void boxClassHasOneTypeParameter() {\n        var typeParameters = Box.class.getTypeParameters();\n\n        assertThat(typeParameters.length).isEqualTo(1);\n    }\n\n    @Test\n    @Order(2)\n    @DisplayName(\"Type parameter is called \\\"T\\\"\")\n    void typeParameterIsCalledT() {\n        var typeParameter = Box.class.getTypeParameters()[0];\n\n        assertThat(typeParameter.getName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(3)\n    @DisplayName(\"Type parameter \\\"T\\\" is not bounded\")\n    void typeParameterIsNotBounded() {\n        var typeParameter = Box.class.getTypeParameters()[0];\n\n        assertThat(typeParameter.getBounds()).hasSize(1);\n        assertThat(typeParameter.getBounds()[0].getTypeName()).isEqualTo(Object.class.getTypeName());\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(4)\n    @DisplayName(\"Field \\\"value\\\" is \\\"T\\\"\")\n    void valueFieldIsGeneric() {\n        var valueField = Box.class.getDeclaredField(\"value\");\n        var genericType = valueField.getGenericType();\n\n        assertThat(genericType.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(5)\n    @DisplayName(\"Constructor parameter type is \\\"T\\\"\")\n    void constructorParameterIsGeneric() {\n        var constructor = Box.class.getDeclaredConstructors()[0];\n        assert (constructor.getParameters().length == 1);\n        var parameter = constructor.getParameters()[0];\n\n        assertThat(parameter.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(6)\n    @DisplayName(\"Getter return type is \\\"T\\\"\")\n    void getterReturnTypeIsGeneric() {\n        var getter = Box.class.getDeclaredMethod(\"getValue\");\n\n        assertThat(getter.getGenericReturnType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(7)\n    @DisplayName(\"Setter parameter type is \\\"T\\\"\")\n    void setterParameterIsGeneric() {\n        var setter = Box.class.getDeclaredMethod(\"setValue\", Object.class);\n        assert (setter.getParameters().length == 1);\n        var parameter = setter.getParameters()[0];\n\n        assertThat(parameter.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n}\n"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Crazy Generics\n#### Learn tricky nuances and become the master of type parametrization in Java 💪\n\n### WHY ❓\nGenerics in Java provide **safe type parametrization.** Sou can write the logic of a method or a whole class and \n**reuse it for different types**. Before Java 5 (when generics were introduced), common logic could be reused as well. \nHowever, it required **using `Object` everywhere and casting it everytime you want to get an actual type**.\nIt **could easily cause `ClassCastException` at runtime which is not safe.**  When using generics, it's still the same `Object`,\nbut **now compiler creates casts for you implicitly, and makes sure you didn't mess up with types**, which **moves errors from\nruntime to compile time and makes type parametrization safer.**\n\n### Objectives\n* create a generic class with **simple type parameter** ✅\n* create a generic class with **bounded type parameter** ✅\n* create a generic class with **multiples type parameter** ✅\n* create a generic class with **recursively bounded type parameter** ✅\n* create a **generic method** ✅\n* create a method that accepts a generic class on any type using **wildcard** ✅\n* create method parameter using **bounded wildcard** ✅\n\n### Related materials ℹ️\n* [Effective Java, Chapter 5 – Generics](https://read.amazon.com/kp/embed?asin=B078H61SCH&preview=newtab&linkCode=kpe&ref_=cm_sw_r_kb_dp_SADNB2C41TWARGY4QGKZ) 📘\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/EaL5KsSlEQM/0.jpg)](https://www.youtube.com/watch?v=EaL5KsSlEQM)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>1-0-java-basics</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>1-3-1-crazy-generics</artifactId>\n    \n</project>"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java",
    "content": "package com.bobocode.basics;\n\nimport com.bobocode.basics.util.BaseEntity;\nimport com.bobocode.util.ExerciseNotCompletedException;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * {@link CrazyGenerics} is an exercise class. It consists of classes, interfaces and methods that should be updated\n * using generics.\n * <p>\n * TODO: go step by step from top to bottom. Read the java doc, write code and run CrazyGenericsTest to verify your impl\n * <p>\n * Hint: in some cases you will need to refactor the code, like replace {@link Object} with a generic type. In order\n * cases you will need to add new fields, create new classes, or add new methods. Always try to read java doc and update\n * the code according to it.\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @author Taras Boychuk\n */\npublic class CrazyGenerics {\n    /**\n     * {@link Sourced} is a container class that allows storing any object along with the source of that data.\n     * The value type can be specified by a type parameter \"T\".\n     *\n     * @param <T> – value type\n     */\n    @Data\n    public static class Sourced { // todo: refactor class to introduce type parameter and make value generic\n        private Object value;\n        private String source;\n    }\n\n    /**\n     * {@link Limited} is a container class that allows storing an actual value along with possible min and max values.\n     * It is special form of triple. All three values have a generic type that should be a subclass of {@link Number}.\n     *\n     * @param <T> – actual, min and max type\n     */\n    @Data\n    public static class Limited {\n        // todo: refactor class to introduce type param bounded by number and make fields generic numbers\n        private final Object actual;\n        private final Object min;\n        private final Object max;\n    }\n\n    /**\n     * {@link Converter} interface declares a typical contract of a converter. It works with two independent generic types.\n     * It defines a convert method which accepts one parameter of one type and returns a converted result of another type.\n     *\n     * @param <T> – source object type\n     * @param <R> - converted result type\n     */\n    public interface Converter { // todo: introduce type parameters\n        // todo: add convert method\n    }\n\n    /**\n     * {@link MaxHolder} is a container class that keeps track of the maximum value only. It works with comparable objects\n     * and allows you to put new values. Every time you put a value, it is stored only if the new value is greater\n     * than the current max.\n     *\n     * @param <T> – value type\n     */\n    public static class MaxHolder { // todo: refactor class to make it generic\n        private Object max;\n\n        public MaxHolder(Object max) {\n            this.max = max;\n        }\n\n        /**\n         * Puts a new value to the holder. A new value is stored to the max, only if it is greater than current max value.\n         *\n         * @param val a new value\n         */\n        public void put(Object val) {\n            throw new ExerciseNotCompletedException(); // todo: update parameter and implement the method\n        }\n\n        public Object getMax() {\n            return max;\n        }\n    }\n\n    /**\n     * {@link StrictProcessor} defines a contract of a processor that can process only objects that are {@link Serializable}\n     * and {@link Comparable}.\n     *\n     * @param <T> – the type of objects that can be processed\n     */\n    interface StrictProcessor { // todo: make it generic\n        void process(Object obj);\n    }\n\n    /**\n     * {@link CollectionRepository} defines a contract of a runtime store for entities based on any {@link Collection}.\n     * It has methods that allow to save new entity, and get whole collection.\n     *\n     * @param <T> – a type of the entity that should be a subclass of {@link BaseEntity}\n     * @param <C> – a type of any collection\n     */\n    interface CollectionRepository { // todo: update interface according to the javadoc\n        void save(Object entity);\n\n        Collection<Object> getEntityCollection();\n    }\n\n    /**\n     * {@link ListRepository} extends {@link CollectionRepository} but specifies the underlying collection as\n     * {@link java.util.List}.\n     *\n     * @param <T> – a type of the entity that should be a subclass of {@link BaseEntity}\n     */\n    interface ListRepository { // todo: update interface according to the javadoc\n    }\n\n    /**\n     * {@link ComparableCollection} is a {@link Collection} that can be compared by size. It extends a {@link Collection}\n     * interface and {@link Comparable} interface, and provides a default implementation of a compareTo method that\n     * compares collections sizes.\n     * <p>\n     * Please note that size does not depend on the elements type, so it is allowed to compare collections of different\n     * element types.\n     *\n     * @param <E> a type of collection elements\n     */\n    interface ComparableCollection { // todo: refactor it to make generic and provide a default impl of compareTo\n    }\n\n    /**\n     * {@link CollectionUtil} is an util class that provides various generic helper methods.\n     */\n    static class CollectionUtil {\n        static final Comparator<BaseEntity> CREATED_ON_COMPARATOR = Comparator.comparing(BaseEntity::getCreatedOn);\n\n        /**\n         * An util method that allows to print a dashed list of elements\n         *\n         * @param list\n         */\n        public static void print(List<Integer> list) {\n            // todo: refactor it so the list of any type can be printed, not only integers\n            list.forEach(element -> System.out.println(\" – \" + element));\n        }\n\n        /**\n         * Util method that check if provided collection has new entities. An entity is any object\n         * that extends {@link BaseEntity}. A new entity is an entity that does not have an id assigned.\n         * (In other word, which id value equals null).\n         *\n         * @param entities provided collection of entities\n         * @return true if at least one of the elements has null id\n         */\n        public static boolean hasNewEntities(Collection<BaseEntity> entities) {\n            throw new ExerciseNotCompletedException(); // todo: refactor parameter and implement method\n        }\n\n        /**\n         * Util method that checks if a provided collection of entities is valid. An entity is any subclass of\n         * a {@link BaseEntity} A validation criteria can be different for different cases, so it is passed\n         * as second parameter.\n         *\n         * @param entities            provided collection of entities\n         * @param validationPredicate criteria for validation\n         * @return true if all entities fit validation criteria\n         */\n        public static boolean isValidCollection() {\n            throw new ExerciseNotCompletedException(); // todo: add method parameters and implement the logic\n        }\n\n        /**\n         * hasDuplicates is a generic util method checks if a list of entities contains target entity more than once.\n         * In other words, it checks if target entity has duplicates in the provided list. A duplicate is an entity that\n         * has the same UUID.\n         *\n         * @param entities     given list of entities\n         * @param targetEntity a target entity\n         * @param <T>          entity type\n         * @return true if entities list contains target entity more than once\n         */\n        public static boolean hasDuplicates() {\n            throw new ExerciseNotCompletedException(); // todo: update method signature and implement it\n        }\n\n        /**\n         * findMax is a generic util method that accepts an {@link Iterable} and {@link Comparator} and returns an\n         * optional object, that has maximum \"value\" based on the given comparator.\n         *\n         * @param elements   provided iterable of elements\n         * @param comparator an object that will be used to compare elements\n         * @param <T>        type of elements\n         * @return optional max value\n         */\n        // todo: create a method and implement its logic manually without using util method from JDK\n\n        /**\n         * findMostRecentlyCreatedEntity is a generic util method that accepts a collection of entities and returns the\n         * one that is the most recently created. If collection is empty,\n         * it throws {@link java.util.NoSuchElementException}.\n         * <p>\n         * This method reuses findMax method and passes entities along with prepare comparator instance,\n         * that is stored as constant CREATED_ON_COMPARATOR.\n         *\n         * @param entities provided collection of entities\n         * @param <T>      entity type\n         * @return an entity from the given collection that has the max createdOn value\n         */\n        // todo: create a method according to JavaDoc and implement it using previous method\n\n        /**\n         * An util method that allows to swap two elements of any list. It changes the list so the element with the index\n         * i will be located on index j, and the element with index j, will be located on the index i.\n         * Please note that in order to make it convenient and simple, it DOES NOT declare any type parameter.\n         *\n         * @param elements a list of any given type\n         * @param i        index of the element to swap\n         * @param j        index of the other element to swap\n         */\n        public static void swap(List<?> elements, int i, int j) {\n            Objects.checkIndex(i, elements.size());\n            Objects.checkIndex(j, elements.size());\n            throw new ExerciseNotCompletedException(); // todo: complete method implementation \n        }\n\n    }\n}\n"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/util/BaseEntity.java",
    "content": "package com.bobocode.basics.util;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\n\nimport java.time.LocalDateTime;\nimport java.util.UUID;\n\n@Getter\n@NoArgsConstructor\n@AllArgsConstructor\npublic abstract class BaseEntity {\n    protected UUID uuid;\n    protected LocalDateTime createdOn;\n\n    public BaseEntity(UUID uuid) {\n        this.uuid = uuid;\n        this.createdOn = LocalDateTime.now();\n    }\n}\n"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java",
    "content": "package com.bobocode.basics;\n\nimport com.bobocode.basics.CrazyGenerics.*;\nimport com.bobocode.basics.util.BaseEntity;\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.mockito.Mockito;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\nimport java.time.LocalDateTime;\nimport java.util.*;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport static java.time.temporal.ChronoUnit.*;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.mockito.Mockito.when;\n\n/**\n * A reflection-based test class for {@link CrazyGenerics}.\n * <p>\n * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.\n *\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class CrazyGenericsTest {\n    final String TYPE_PARAMETER_NAME = \"T\";\n    final String SECOND_TYPE_PARAMETER_NAME = \"R\";\n    final String COLLECTION_ELEMENT_TYPE_PARAMETER_NAME = \"E\";\n\n    @Test\n    @Order(1)\n    @DisplayName(\"Sourced class has one type parameter\")\n    void sourcedClassHasOneTypeParameter() {\n        var typeParameters = Sourced.class.getTypeParameters();\n\n        assertThat(typeParameters).hasSize(1);\n    }\n\n    @Test\n    @Order(2)\n    @DisplayName(\"Sourced class type parameter is called \\\"T\\\"\")\n    void sourcedClassTypeParameterIsCalledT() {\n        var typeParameter = Sourced.class.getTypeParameters()[0];\n\n        assertThat(typeParameter.getName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(3)\n    @DisplayName(\"Sourced class type parameter \\\"T\\\" is not bounded\")\n    void sourcedClassTypeParameterIsNotBounded() {\n        var typeParameter = Sourced.class.getTypeParameters()[0];\n\n        assertThat(typeParameter.getBounds()).hasSize(1);\n        assertThat(typeParameter.getBounds()[0].getTypeName()).isEqualTo(Object.class.getTypeName());\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(4)\n    @DisplayName(\"Sourced class field \\\"value\\\" has generic type \\\"T\\\"\")\n    void valueFieldIsGeneric() {\n        var valueField = Sourced.class.getDeclaredField(\"value\");\n        var genericType = valueField.getGenericType();\n\n        assertThat(genericType.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n\n    @Test\n    @Order(5)\n    @DisplayName(\"Limited class has one type parameter\")\n    void limitedClassHasOneTypeParameter() {\n        var typeParameters = Limited.class.getTypeParameters();\n\n        assertThat(typeParameters).hasSize(1);\n    }\n\n    @Test\n    @Order(6)\n    @DisplayName(\"Limited class type parameter is bounded by Number\")\n    void limitedClassTypeParameterIsBoundedByNumber() {\n        var typeParameters = Limited.class.getTypeParameters();\n        var typeParam = typeParameters[0];\n        assert (typeParam.getBounds().length == 1);\n        var boundType = typeParam.getBounds()[0];\n\n        assertThat(boundType.getTypeName()).isEqualTo(Number.class.getTypeName());\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(7)\n    @DisplayName(\"Limited class fields have generic type \\\"T\\\"\")\n    void limitedClassFieldsAreGeneric() {\n        var fields = Limited.class.getDeclaredFields();\n\n        for (var f : fields) {\n            assertThat(f.getGenericType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n        }\n    }\n\n    @Test\n    @Order(8)\n    @DisplayName(\"Converter interface has two type parameters\")\n    void converterInterfaceHasTwoTypeParameters() {\n        var typeParameters = Converter.class.getTypeParameters();\n\n        assertThat(typeParameters).hasSize(2);\n    }\n\n    @Test\n    @Order(9)\n    @DisplayName(\"Converter interface first type parameter is called \\\"T\\\"\")\n    void converterInterfaceFirstTypeParameterIsCalledT() {\n        var typeParameters = Converter.class.getTypeParameters();\n        var firstTypeParam = typeParameters[0];\n\n        assertThat(firstTypeParam.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(10)\n    @DisplayName(\"Converter interface second type parameter is called \\\"R\\\"\")\n    void converterInterfaceSecondTypeParameterIsCalledR() {\n        var typeParameters = Converter.class.getTypeParameters();\n        var firstTypeParam = typeParameters[1];\n\n        assertThat(firstTypeParam.getTypeName()).isEqualTo(SECOND_TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(11)\n    @DisplayName(\"Convert method parameter \\\"obj\\\" has type \\\"T\\\"\")\n    void converterMethodParameterHasTypeT() {\n        var convertMethod = Converter.class.getDeclaredMethod(\"convert\", Object.class);\n        var objParam = convertMethod.getParameters()[0];\n\n        assertThat(objParam.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(12)\n    @DisplayName(\"Convert method return type is \\\"R\\\"\")\n    void converterMethodReturnTypeIsR() {\n        var convertMethod = Converter.class.getDeclaredMethod(\"convert\", Object.class);\n\n        assertThat(convertMethod.getGenericReturnType().getTypeName()).isEqualTo(SECOND_TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(13)\n    @DisplayName(\"MaxHolder class has one type parameter\")\n    void maxHolderClassHasOneTypeParameter() {\n        var typeParameters = MaxHolder.class.getTypeParameters();\n\n        assertThat(typeParameters).hasSize(1);\n    }\n\n    @Test\n    @Order(14)\n    @DisplayName(\"MaxHolder class type parameter is called \\\"T\\\"\")\n    void maxHolderClassTypeParameterIsCalledT() {\n        var typeParameter = MaxHolder.class.getTypeParameters()[0];\n\n        assertThat(typeParameter.getName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(15)\n    @DisplayName(\"MaxHolder type parameter is bound by Comparable\")\n    void maxHolderClassTypeParameterShouldBeBoundByComparableT() {\n        var typeParameters = MaxHolder.class.getTypeParameters();\n        var typeParam = typeParameters[0];\n        var boundType = typeParam.getBounds()[0];\n\n        var expectedBoundTypeName = String.format(\"%s<? super %s>\", Comparable.class.getTypeName(), TYPE_PARAMETER_NAME);\n        assertThat(boundType.getTypeName()).isEqualTo(expectedBoundTypeName);\n    }\n\n    @Test\n    @Order(16)\n    @DisplayName(\"MaxHolder field has type \\\"T\\\"\")\n    void maxHolderFieldHasTypeT() {\n        var typeParameter = MaxHolder.class.getTypeParameters()[0];\n\n        assertThat(typeParameter.getName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(17)\n    @SneakyThrows\n    @DisplayName(\"MaxHolder constructor param has type \\\"T\\\"\")\n    void maxHolderConstructorParamHasTypeT() {\n        var constructor = MaxHolder.class.getConstructors()[0];\n        assert (constructor.getParameters().length == 1);\n        var param = constructor.getParameters()[0];\n\n        assertThat(param.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(18)\n    @SneakyThrows\n    @DisplayName(\"MaxHolder put method param has type \\\"T\\\"\")\n    void maxHolderPutMethodParamHasTypeT() {\n        var putMethod = getMethodByName(MaxHolder.class, \"put\");\n        var param = putMethod.getParameters()[0];\n\n        assertThat(param.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(19)\n    @SneakyThrows\n    @DisplayName(\"MaxHolder getMax return type is \\\"T\\\"\")\n    void maxHolderGetMaxReturnTypeIsT() {\n        var getMaxMethod = MaxHolder.class.getDeclaredMethod(\"getMax\");\n\n        assertThat(getMaxMethod.getGenericReturnType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(20)\n    @SneakyThrows\n    @DisplayName(\"MaxHolder put stores argument value when it's greater than max\")\n    void maxHolderPut() {\n        Constructor<?> constructor = MaxHolder.class.getConstructors()[0];\n        var maxHolder = constructor.newInstance(10);\n        var putMethod = getMethodByName(MaxHolder.class, \"put\");\n        var getMaxMethod = MaxHolder.class.getDeclaredMethod(\"getMax\");\n\n        putMethod.invoke(maxHolder, 11);\n\n        assertThat(getMaxMethod.invoke(maxHolder)).isEqualTo(11);\n    }\n\n    @Test\n    @Order(21)\n    @SneakyThrows\n    @DisplayName(\"MaxHolder put keeps old value when argument is less than max\")\n    void maxHolderPutWhenArgumentIsLessThanMax() {\n        Constructor<?> constructor = MaxHolder.class.getConstructors()[0];\n        var maxHolder = constructor.newInstance(10);\n        var putMethod = getMethodByName(MaxHolder.class, \"put\");\n        var getMaxMethod = MaxHolder.class.getDeclaredMethod(\"getMax\");\n\n        putMethod.invoke(maxHolder, 9);\n\n        assertThat(getMaxMethod.invoke(maxHolder)).isEqualTo(10);\n    }\n\n    @Test\n    @Order(22)\n    @DisplayName(\"StrictProcessor class has one generic type\")\n    void strictProcessorHasOneGenericType() {\n        var typeParameters = StrictProcessor.class.getTypeParameters();\n\n        assertThat(typeParameters).hasSize(1);\n    }\n\n    @Test\n    @Order(23)\n    @DisplayName(\"StrictProcessor type parameter is called \\\"T\\\"\")\n    void strictProcessorTypeParameterIsCalledT() {\n        var typeParameters = StrictProcessor.class.getTypeParameters();\n        var typePram = typeParameters[0];\n\n        assertThat(typePram.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(24)\n    @DisplayName(\"StrictProcessor type parameter is bound by both Serializable and Comparable<T>\")\n    void strictProcessorTypeParameterIsBoundBySerializableAndComparable() {\n        var typeParameters = StrictProcessor.class.getTypeParameters();\n        var typeParam = typeParameters[0];\n        assertThat(typeParam.getBounds())\n                .hasSize(2)\n                .extracting(Type::getTypeName)\n                .containsExactlyInAnyOrder(Serializable.class.getTypeName(),\n                        String.format(\"%s<? super %s>\", Comparable.class.getTypeName(), TYPE_PARAMETER_NAME));\n    }\n\n    @Test\n    @Order(25)\n    @DisplayName(\"StrictProcessor process method parameter has type \\\"T\\\"\")\n    void strictProcessorProcessMethodParameterHasTypeT() {\n        var processMethod = getMethodByName(StrictProcessor.class, \"process\");\n\n        assert (processMethod.getParameters().length == 1);\n        var processMethodParam = processMethod.getParameters()[0];\n\n        assertThat(processMethodParam.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(26)\n    @DisplayName(\"CollectionRepository has two type parameters\")\n    void collectionRepositoryHasTwoTypeParameters() {\n        var typeParameters = CollectionRepository.class.getTypeParameters();\n\n        assertThat(typeParameters).hasSize(2);\n    }\n\n    @Test\n    @Order(27)\n    @DisplayName(\"CollectionRepository first type parameter is called \\\"T\\\"\")\n    void collectionRepositoryFirstTypeParameterIsCalledT() {\n        var typeParam = CollectionRepository.class.getTypeParameters()[0];\n\n        assertThat(typeParam.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(28)\n    @DisplayName(\"CollectionRepository first type parameter is bounded by BaseEntity\")\n    void collectionRepositoryFirstTypeParameterIsBoundedByBaseEntity() {\n        var typeParam = CollectionRepository.class.getTypeParameters()[0];\n        var boundType = typeParam.getBounds()[0];\n\n        assertThat(boundType.getTypeName()).isEqualTo(BaseEntity.class.getTypeName());\n    }\n\n    @Test\n    @Order(29)\n    @DisplayName(\"CollectionRepository second type parameter is called \\\"C\\\"\")\n    void collectionRepositorySecondTypeParameterIsCalledT() {\n        var typeParam = CollectionRepository.class.getTypeParameters()[1];\n\n        assertThat(typeParam.getTypeName()).isEqualTo(\"C\");\n    }\n\n    @Test\n    @Order(30)\n    @DisplayName(\"CollectionRepository second type parameter is bounded by Collection<T>\")\n    void collectionRepositorySecondTypeParameterIsBoundedByCollection() {\n        var typeParam = CollectionRepository.class.getTypeParameters()[1];\n        var boundType = typeParam.getBounds()[0];\n\n        assertThat(boundType.getTypeName())\n                .isEqualTo(String.format(\"%s<T>\", Collection.class.getTypeName()));\n    }\n\n    @Test\n    @Order(31)\n    @DisplayName(\"CollectionRepository save method param has type \\\"T\\\"\")\n    @SneakyThrows\n    void collectionRepositorySaveMethodParameterHasTypeT() {\n        var saveMethod = getMethodByName(CollectionRepository.class, \"save\");\n\n        var methodParam = saveMethod.getParameters()[0];\n\n        assertThat(methodParam.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(32)\n    @DisplayName(\"CollectionRepository getEntityCollection method has return type \\\"C\\\"\")\n    @SneakyThrows\n    void collectionRepositoryGetCollectionMethodHasReturnTypeC() {\n        var getEntityCollectionMethod = CollectionRepository.class.getMethod(\"getEntityCollection\");\n\n        assertThat(getEntityCollectionMethod.getGenericReturnType().getTypeName()).isEqualTo(\"C\");\n    }\n\n    @Test\n    @Order(33)\n    @DisplayName(\"ListRepository extends CollectionRepository\")\n    void listRepositoryExtendsCollectionRepository() {\n        var superInterface = ListRepository.class.getInterfaces()[0];\n\n        assertThat(superInterface.getTypeName()).isEqualTo(CollectionRepository.class.getTypeName());\n    }\n\n    @Test\n    @Order(34)\n    @DisplayName(\"ListRepository has one type parameter\")\n    void listRepositoryHasOneTypeParameter() {\n        var typeParams = ListRepository.class.getTypeParameters();\n\n        assertThat(typeParams).hasSize(1);\n    }\n\n    @Test\n    @Order(35)\n    @DisplayName(\"ListRepository type parameter is called \\\"T\\\"\")\n    void listRepositoryTypeParameterIsCalledT() {\n        var typeParam = ListRepository.class.getTypeParameters()[0];\n\n        assertThat(typeParam.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(36)\n    @DisplayName(\"ListRepository type parameter is bound by BaseEntity\")\n    void listRepositoryTypeParameterIsBoundByBaseEntity() {\n        var typeParam = ListRepository.class.getTypeParameters()[0];\n        var boundType = typeParam.getBounds()[0];\n\n        assertThat(boundType.getTypeName()).isEqualTo(BaseEntity.class.getTypeName());\n    }\n\n    @Test\n    @Order(37)\n    @DisplayName(\"ListRepository parent interface has specified two types\")\n    void listRepositoryParentInterfaceHasSpecifiedTwoTypes() {\n        var collectionInterface = ListRepository.class.getGenericInterfaces()[0];\n\n        assertThat(collectionInterface.getTypeName())\n                .isEqualTo(String.format(\"%s<T, %s<T>>\", CollectionRepository.class.getTypeName(), List.class.getTypeName()));\n    }\n\n    @Test\n    @Order(38)\n    @DisplayName(\"ComparableCollection has one type parameter \\\"E\\\"\")\n    void comparableCollectionIsGeneric() {\n        var typeParameters = ComparableCollection.class.getTypeParameters();\n\n        assertThat(typeParameters).hasSize(1);\n        assertThat(typeParameters[0].getName()).isEqualTo(COLLECTION_ELEMENT_TYPE_PARAMETER_NAME);\n    }\n\n    @Test\n    @Order(39)\n    @DisplayName(\"ComparableCollection extends Collection\")\n    void comparableCollectionExtendsCollection() {\n        var collectionInterface = Arrays.stream(ComparableCollection.class.getInterfaces())\n                .filter(it -> it.getTypeName().equals(Collection.class.getTypeName()))\n                .findAny()\n                .orElseThrow();\n\n        assertThat(collectionInterface).isNotNull();\n    }\n\n    @Test\n    @Order(40)\n    @DisplayName(\"Type parameter is specified for a super interface Collection\")\n    void comparableCollectionExtendsCollectionOfTheSameElementsType() {\n        var collectionInterface = Arrays.stream(ComparableCollection.class.getGenericInterfaces())\n                .filter(it -> it.getTypeName().equals(\n                        String.format(\"%s<%s>\", Collection.class.getTypeName(), COLLECTION_ELEMENT_TYPE_PARAMETER_NAME)\n                ))\n                .findAny()\n                .orElseThrow();\n\n        assertThat(collectionInterface).isNotNull();\n    }\n\n    @Test\n    @Order(41)\n    @DisplayName(\"ComparableCollection extends Comparable\")\n    void comparableCollectionExtendsComparable() {\n        var comparableInterface = Arrays.stream(ComparableCollection.class.getInterfaces())\n                .filter(it -> it.getTypeName().equals(Comparable.class.getTypeName()))\n                .findAny()\n                .orElseThrow();\n\n        assertThat(comparableInterface).isNotNull();\n    }\n\n    @Test\n    @Order(42)\n    @DisplayName(\"Collection of any type is specified as type parameter for a super interface Comparable\")\n    void comparableCollectionExtendsComparableOfCollectionOfAnyType() {\n        var comparableInterface = Arrays.stream(ComparableCollection.class.getGenericInterfaces())\n                .filter(it -> it.getTypeName().equals(\n                        String.format(\"%s<%s<?>>\", Comparable.class.getTypeName(), Collection.class.getTypeName())\n                ))\n                .findAny()\n                .orElseThrow();\n\n        assertThat(comparableInterface).isNotNull();\n    }\n\n    @Test\n    @Order(43)\n    @DisplayName(\"Method compareTo is overridden\")\n    void comparableCollectionOverridesCompareToMethod() {\n        var compareToMethod = Arrays.stream(ComparableCollection.class.getDeclaredMethods())\n                .filter(m -> m.getName().equals(\"compareTo\"))\n                .findAny();\n\n        assertThat(compareToMethod).isPresent();\n    }\n\n    @Test\n    @Order(44)\n    @DisplayName(\"ComparableCollection provides a default impl of compareTo method\")\n    void compareToProvidesDefaultImpl() {\n        var compareToMethod = getMethodByName(ComparableCollection.class, \"compareTo\");\n\n        assertThat(compareToMethod.isDefault()).isTrue();\n    }\n\n    @Test\n    @Order(45)\n    @DisplayName(\"A parameter of method compareTo is a collection of elements of any type\")\n    void compareToParamIsACollectionOfAnyType() {\n        var compareToMethod = getMethodByName(ComparableCollection.class, \"compareTo\");\n        var collectionParam = compareToMethod.getParameters()[0];\n\n        assertThat(collectionParam.getParameterizedType().getTypeName())\n                .isEqualTo(String.format(\"%s<?>\", Collection.class.getTypeName()));\n    }\n\n    @Order(46)\n    @DisplayName(\"Method compareTo compares collection size\")\n    @SneakyThrows\n    @ParameterizedTest\n    @ValueSource(ints = {0, 5, 10})\n    void compareToComparesSize(int size) {\n        var compareToMethod = getMethodByName(ComparableCollection.class, \"compareTo\");\n        var compCollectionMock = Mockito.spy(ComparableCollection.class);\n        var sizeMethod = getMethodByName(ComparableCollection.class, \"size\");\n\n        when(sizeMethod.invoke(compCollectionMock)).thenReturn(size);\n        var list = List.of(1, 2, 3, 4, 5);\n\n        assertThat(compareToMethod.invoke(compCollectionMock, list))\n                .isEqualTo(Integer.compare(size, list.size()));\n    }\n\n    @Test\n    @Order(47)\n    @DisplayName(\"CollectionUtil is not a generic class\")\n    void collectionUtilIsNotAGenericClass() {\n        var typeParams = CollectionUtil.class.getTypeParameters();\n\n        assertThat(typeParams).isEmpty();\n    }\n\n    @Test\n    @Order(48)\n    @DisplayName(\"Method print param is a list of any type declared as unbounded wildcard\")\n    void printParamIsAListOfAnyType() {\n        var printMethod = getMethodByName(CollectionUtil.class, \"print\");\n        var listParam = printMethod.getParameters()[0];\n        var typeName = listParam.getParameterizedType().getTypeName();\n\n        assertThat(typeName).isEqualTo(String.format(\"%s<?>\", List.class.getTypeName()));\n\n    }\n\n    @Test\n    @Order(49)\n    @DisplayName(\"Method hasNewEntities accepts a collection of any entities (BaseEntity subclasses)\")\n    void hasNewEntitiesMethodParamIsAGenericCollectionOfEntities() {\n        var hasNewEntitiesMethod = getMethodByName(CollectionUtil.class, \"hasNewEntities\");\n\n        var entitiesParam = hasNewEntitiesMethod.getParameters()[0];\n\n        assertThat(entitiesParam.getParameterizedType().getTypeName())\n                .isEqualTo(String.format(\"%s<? extends %s>\", Collection.class.getTypeName(), BaseEntity.class.getTypeName()));\n    }\n\n    @ParameterizedTest\n    @Order(50)\n    @MethodSource(\"hasNewEntitiesArgs\")\n    @DisplayName(\"Method hasNewEntities checks id values\")\n    @SneakyThrows\n    void hasNewEntitiesChecksIds(Collection<BaseEntity> entities, boolean result) {\n        var hasNewEntitiesMethod = getMethodByName(CollectionUtil.class, \"hasNewEntities\");\n\n        boolean hasNewEntities = (boolean) hasNewEntitiesMethod.invoke(null, entities);\n\n        assertThat(hasNewEntities).isEqualTo(result);\n    }\n\n    private Method getMethodByName(Class<?> clazz, String methodName) {\n        return Arrays.stream(clazz.getMethods())\n                .filter(m -> m.getName().equals(methodName))\n                .findAny().orElseThrow();\n    }\n\n    static Stream<Arguments> hasNewEntitiesArgs() {\n        var newEntity = Mockito.mock(BaseEntity.class);\n        when(newEntity.getUuid()).thenReturn(null);\n        var oldEntity = Mockito.mock(BaseEntity.class);\n        when(oldEntity.getUuid()).thenReturn(UUID.randomUUID());\n\n        return Stream.of(\n                arguments(List.of(newEntity, newEntity), true),\n                arguments(List.of(newEntity, oldEntity), true),\n                arguments(List.of(oldEntity, oldEntity), false)\n        );\n    }\n\n    @Test\n    @Order(51)\n    @DisplayName(\"Method isValidCollection accepts a collection of any entities as a first param\")\n    void isValidCollectionMethodFirstParamIsAGenericCollectionOfEntities() {\n        var isValidCollectionMethod = getMethodByName(CollectionUtil.class, \"isValidCollection\");\n\n        var entitiesParam = isValidCollectionMethod.getParameters()[0];\n\n        assertThat(entitiesParam.getParameterizedType().getTypeName())\n                .isEqualTo(String.format(\"%s<? extends %s>\", Collection.class.getTypeName(), BaseEntity.class.getTypeName()));\n    }\n\n    @Test\n    @Order(52)\n    @DisplayName(\"Method isValidCollection accepts a predicate of any BaseEntity superclasses as a second param\")\n    void isValidCollectionMethodSecondParamIsAnyBaseEntitySuperClass() {\n        var isValidCollectionMethod = getMethodByName(CollectionUtil.class, \"isValidCollection\");\n\n        var validationPredicate = isValidCollectionMethod.getParameters()[1];\n\n        assertThat(validationPredicate.getParameterizedType().getTypeName())\n                .isEqualTo(String.format(\"%s<? super %s>\", Predicate.class.getTypeName(), BaseEntity.class.getTypeName()));\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"isValidCollectionArgs\")\n    @Order(53)\n    @DisplayName(\"Method isValidCollection returns true when all entities pass validation predicate\")\n    @SneakyThrows\n    void isValidCollectionReturnsTrueWhenAllEntitiesPassValidationPredicate(List<TestEntity> entities,\n                                                                            Predicate<TestEntity> validationPredicate,\n                                                                            boolean isValid) {\n        var isValidCollectionMethod = getMethodByName(CollectionUtil.class, \"isValidCollection\");\n\n        boolean result = (boolean) isValidCollectionMethod.invoke(null, entities, validationPredicate);\n\n        assertThat(result).isEqualTo(isValid);\n    }\n\n    private static Stream<Arguments> isValidCollectionArgs() {\n        var now = LocalDateTime.now();\n        Predicate<BaseEntity> notNullIdValidationPredicate = e -> e.getUuid() != null;\n        Predicate<BaseEntity> pastCreatedDateValidationPredicate = e -> e.getCreatedOn().compareTo(now) < 0;\n\n        return Stream.of(\n                arguments(List.of(new TestEntity(), new TestEntity()), notNullIdValidationPredicate, true),\n                arguments(List.of(new TestEntity(), new TestEntity((UUID) null)), notNullIdValidationPredicate, false),\n                arguments(\n                        List.of(\n                                new TestEntity(now.minus(1, DAYS)),\n                                new TestEntity((now.minus(1, MONTHS)))\n                        ),\n                        pastCreatedDateValidationPredicate,\n                        true\n                ),\n                arguments(\n                        List.of(\n                                new TestEntity(now.minus(1, DAYS)),\n                                new TestEntity((now.plus(2, DAYS)))\n                        ),\n                        pastCreatedDateValidationPredicate,\n                        false\n                )\n        );\n    }\n\n\n    @Test\n    @Order(54)\n    @DisplayName(\"hasDuplicates is a generic method\")\n    void hasDuplicatesIsAGenericMethod() {\n        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, \"hasDuplicates\");\n\n        assertThat(hasDuplicatesMethod.getTypeParameters()).hasSize(1);\n    }\n\n    @Test\n    @Order(55)\n    @DisplayName(\"hasDuplicates type parameter is called \\\"T\\\"\")\n    void hasDuplicatesTypeParameterIsCalledT() {\n        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, \"hasDuplicates\");\n        var typeParameter = hasDuplicatesMethod.getTypeParameters()[0];\n\n        assertThat(typeParameter.getName()).isEqualTo(\"T\");\n    }\n\n    @Test\n    @Order(56)\n    @DisplayName(\"hasDuplicates type parameter is bounded by BaseEntity\")\n    void hasDuplicatesTypeParameterIsBoundedByBaseEntity() {\n        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, \"hasDuplicates\");\n        var typeParameter = hasDuplicatesMethod.getTypeParameters()[0];\n        Type bound = typeParameter.getBounds()[0];\n\n        assertThat(bound.getTypeName()).isEqualTo(BaseEntity.class.getTypeName());\n    }\n\n    @ParameterizedTest\n    @Order(57)\n    @MethodSource(\"hasDuplicatesArgs\")\n    @DisplayName(\"hasDuplicates checks entity duplicates by UUID\")\n    @SneakyThrows\n    void hasDuplicatesChecksEntitiesByUUID(List<? extends BaseEntity> entities, BaseEntity targetEntity, Boolean hasDuplicates) {\n        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, \"hasDuplicates\");\n\n        var result = hasDuplicatesMethod.invoke(null, entities, targetEntity);\n\n        assertThat(result).isEqualTo(hasDuplicates);\n    }\n\n    static Stream<Arguments> hasDuplicatesArgs() {\n        var uniqueEntity = new TestEntity(UUID.randomUUID());\n        var duplicateId = UUID.randomUUID();\n        var duplicateEntity1 = new TestEntity(duplicateId);\n        var duplicateEntity2 = new TestEntity(duplicateId);\n\n        return Stream.of(\n                arguments(List.of(uniqueEntity, duplicateEntity1), uniqueEntity, false),\n                arguments(List.of(uniqueEntity, duplicateEntity1, duplicateEntity2), duplicateEntity1, true),\n                arguments(List.of(duplicateEntity1, duplicateEntity2), duplicateEntity1, true)\n        );\n    }\n\n    @Test\n    @Order(58)\n    @DisplayName(\"Method findMax has public access type\")\n    void findMaxHasPublicAccessType() {\n        var findMaxMethod = getMethodByName(CollectionUtil.class, \"findMax\");\n        assertThat(findMaxMethod).isNotNull();\n    }\n\n    @ParameterizedTest\n    @Order(59)\n    @MethodSource(\"findMaxArgs\")\n    @DisplayName(\"Method findMax returns the max value based on given comparator\")\n    @SneakyThrows\n    void findMaxMethodReturnMaxValue(List<?> elements, Comparator<?> comparator, Object maxElement) {\n        var findMaxMethod = getMethodByName(CollectionUtil.class, \"findMax\");\n\n        var result = findMaxMethod.invoke(null, elements, comparator);\n\n        assertThat(result).isEqualTo(Optional.of(maxElement));\n    }\n\n    static Stream<Arguments> findMaxArgs() {\n        var maxEntity = new TestEntity(LocalDateTime.now().plus(10, DAYS));\n        var entities = new ArrayList<>(List.of(new TestEntity(), new TestEntity(), maxEntity, new TestEntity()));\n        var entityCreatedOnComparator = Comparator.comparing(BaseEntity::getCreatedOn);\n        var maxNumber = 100;\n        var numbers = List.of(maxNumber, 5, 20);\n        var intComparator = Comparator.comparingInt(Integer::intValue);\n        var maxWord = \"what's up?\";\n        var greetings = List.of(\"hey\", maxWord, \"hello\");\n        var lengthComparator = Comparator.comparing(String::length);\n\n        return Stream.of(\n                arguments(entities, entityCreatedOnComparator, maxEntity),\n                arguments(numbers, intComparator, maxNumber),\n                arguments(greetings, lengthComparator, maxWord)\n        );\n    }\n\n    @Test\n    @Order(60)\n    @DisplayName(\"Method findMostRecentlyCreatedEntity has public access type\")\n    void findMostRecentlyCreatedEntityHasPublicAccessType() {\n        var findMostRecentlyCreatedEntity = getMethodByName(CollectionUtil.class, \"findMostRecentlyCreatedEntity\");\n        assertThat(findMostRecentlyCreatedEntity).isNotNull();\n    }\n\n    @Test\n    @Order(61)\n    @DisplayName(\"findMostRecentlyCreatedEntity is a generic method that accepts a collection of entities\")\n    void findMostRecentlyCreatedEntityIsAGenericMethod() {\n        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, \"findMostRecentlyCreatedEntity\");\n        var typeParameter = hasDuplicatesMethod.getTypeParameters()[0];\n        var bound = typeParameter.getBounds()[0];\n\n        assertThat(typeParameter.getName()).isEqualTo(\"T\");\n        assertThat(bound.getTypeName()).isEqualTo(BaseEntity.class.getTypeName());\n    }\n\n    @ParameterizedTest\n    @Order(62)\n    @MethodSource(\"findMostRecentlyCreatedEntityArgs\")\n    @DisplayName(\"findMostRecentlyCreatedEntity returns the most recently created entity\")\n    @SneakyThrows\n    void findMostRecentlyCreatedEntityReturnsEntityWithMaxCreatedOnValue(List<? extends BaseEntity> entities,\n                                                                         BaseEntity mostRecentlyCreatedEntity) {\n        var findMostRecentlyCreatedEntityMethod = getMethodByName(CollectionUtil.class, \"findMostRecentlyCreatedEntity\");\n\n        var result = findMostRecentlyCreatedEntityMethod.invoke(null, entities);\n\n        assertThat(result).isEqualTo(mostRecentlyCreatedEntity);\n    }\n\n    static Stream<Arguments> findMostRecentlyCreatedEntityArgs() {\n        var yearAgoEntity = new TestEntity(LocalDateTime.now().minus(1, YEARS));\n        var monthAgoEntity = new TestEntity(LocalDateTime.now().minus(1, MONTHS));\n        var dayAgoEntity = new TestEntity(LocalDateTime.now().minus(1, DAYS));\n\n        return Stream.of(\n                arguments(List.of(yearAgoEntity, monthAgoEntity, dayAgoEntity), dayAgoEntity),\n                arguments(List.of(yearAgoEntity, monthAgoEntity), monthAgoEntity),\n                arguments(List.of(yearAgoEntity, dayAgoEntity), dayAgoEntity),\n                arguments(List.of(dayAgoEntity, monthAgoEntity), dayAgoEntity)\n        );\n    }\n\n    @Test\n    @Order(63)\n    @DisplayName(\"findMostRecentlyCreatedEntity throws exception when collection is empty\")\n    @SneakyThrows\n    void findMostRecentlyCreatedEntityThrowsException() {\n        var findMostRecentlyCreatedEntityMethod = getMethodByName(CollectionUtil.class, \"findMostRecentlyCreatedEntity\");\n\n        assertThatThrownBy(() -> findMostRecentlyCreatedEntityMethod.invoke(null, Collections.emptyList()))\n                .hasCauseInstanceOf(NoSuchElementException.class);\n    }\n\n    @Test\n    @Order(64)\n    @DisplayName(\"Method swap does not declare type parameter\")\n    void swapMethodDoesNotDeclareTypeParameter() {\n        var swapMethod = getMethodByName(CollectionUtil.class, \"swap\");\n        var typeParameters = swapMethod.getTypeParameters();\n\n        assertThat(typeParameters).isEmpty();\n    }\n\n    @Test\n    @Order(65)\n    @DisplayName(\"Method swap change elements by indexes\")\n    @SneakyThrows\n    void swapChangesElements() {\n        var testEntity1 = new TestEntity();\n        var testEntity2 = new TestEntity();\n        var entities = new ArrayList<>(List.of(new TestEntity(), testEntity1, testEntity2, new TestEntity()));\n        var swapMethod = getMethodByName(CollectionUtil.class, \"swap\");\n\n        swapMethod.invoke(null, entities, 1, 2);\n\n        assertThat(entities.get(1)).isEqualTo(testEntity2);\n        assertThat(entities.get(2)).isEqualTo(testEntity1);\n    }\n\n    static class TestEntity extends BaseEntity {\n        public TestEntity() {\n            this(UUID.randomUUID());\n        }\n\n        public TestEntity(UUID uuid) {\n            super(uuid);\n        }\n\n        public TestEntity(LocalDateTime createdOn) {\n            super(UUID.randomUUID(), createdOn);\n        }\n    }\n}\n"
  },
  {
    "path": "1-0-java-basics/1-3-2-heterogeneous-max-holder/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Heterogeneous Max Holder\n#### Improve your generics-related skills implementing a multi-type max holder container 💪\n\n### Objectives\n* create a container class that uses *a type token* ✅\n* declare a generic type parameter that is `Comparable` ✅\n* implement a generic method put that stores max values by its type ✅\n* overload a generic method put with a custom `Comparator` ✅\n* implement a generic method getMax that retrieves a max value by its type ✅\n* wrap a comparator instance to make it null-safe ✅\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/jQnGDgH-Zfg/0.jpg)](https://www.youtube.com/watch?v=jQnGDgH-Zfg)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "1-0-java-basics/1-3-2-heterogeneous-max-holder/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>1-0-java-basics</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>1-3-2-heterogeneous-max-holder</artifactId>\n    \n</project>"
  },
  {
    "path": "1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java",
    "content": "package com.bobocode.basics;\n\nimport java.util.Map;\n\n/**\n * {@link HeterogeneousMaxHolder} is a multi-type container that holds maximum values per each type. It's kind of a\n * key/value map, where the key is a type and the value is the maximum among all values of this type that were put.\n * <p>\n * It's based on the {@link Map} and provides an API that allows to put a value by type, and get a max value by type.\n * <p>\n * <p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @author Taras Boychuk\n */\npublic class HeterogeneousMaxHolder {\n\n    /**\n     * A method put stores a provided value by its type, if the value is greater than the current maximum. In other words, the logic\n     * of this method makes sure that only max value is stored and everything else is ignored.\n     * <p>\n     * If the current max value is less than a provided one, or if it's null, then a provided value gets stored and the old\n     * max is returned. Otherwise, nothing new is added, and the provided value is returned.\n     * <p>\n     * So technically, this method always stores the greater value and returns the smaller one.\n     *\n     * @param key   a provided value type\n     * @param value a value to put\n     * @param <T>   value type parameter\n     * @return a smaller value among the provided value and the current maximum\n     */\n    // todo: implement a method according to javadoc\n\n    /**\n     * An overloaded method put implements the same logic using a custom comparator. A given comparator is wrapped with\n     * a null-safe comparator, considering null smaller than any non-null object.\n     * <p>\n     * All arguments must not be null.\n     *\n     * @param key        a provided value type\n     * @param value      a value to put\n     * @param comparator a custom comparator for the given type\n     * @param <T>        value type parameter\n     * @return a smaller value among the provided value and the current maximum\n     */\n    // todo: implement a method according to javadoc\n\n    /**\n     * A method getMax returns a max value by the given type. If no value is stored by this type, then it returns null.\n     *\n     * @param key a provided value type\n     * @param <T> value type parameter\n     * @return current max value or null\n     */\n    // todo: implement a method according to javadoc\n}\n"
  },
  {
    "path": "1-0-java-basics/1-3-2-heterogeneous-max-holder/src/test/java/com/bobocode/basics/HeterogeneousMaxHolderTest.java",
    "content": "package com.bobocode.basics;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.mockito.Mockito;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.math.BigDecimal;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n\n@TestMethodOrder(OrderAnnotation.class)\nclass HeterogeneousMaxHolderTest {\n    private HeterogeneousMaxHolder heterogeneousMaxHolder = new HeterogeneousMaxHolder();\n    private HeterogeneousMaxHolder heterogeneousMaxHolderMock = Mockito.spy(HeterogeneousMaxHolder.class);\n\n    @Test\n    @Order(1)\n    @DisplayName(\"A class does not declare type parameters\")\n    void classDoesNotDeclareTypeParameters() {\n        var classTypeParams = HeterogeneousMaxHolder.class.getTypeParameters();\n\n        assertThat(classTypeParams).isEmpty();\n    }\n\n    @Test\n    @Order(2)\n    @DisplayName(\"A class has one private field Map\")\n    void classHasOneField() {\n        var fields = HeterogeneousMaxHolder.class.getDeclaredFields();\n\n        assertThat(fields).hasSize(1);\n        assertThat(fields[0].getType()).isEqualTo(Map.class);\n    }\n\n    @Test\n    @Order(3)\n    @DisplayName(\"A Map field declares type arguments\")\n    void mapFieldStoresTypeToObject() {\n        var field = HeterogeneousMaxHolder.class.getDeclaredFields()[0];\n\n        assertThat(field.getGenericType().getTypeName())\n                .isEqualTo(Map.class.getTypeName() + \"<%s, %s>\", Class.class.getTypeName() + \"<?>\", Object.class.getTypeName());\n\n    }\n\n    @Test\n    @Order(4)\n    @DisplayName(\"put method exists\")\n    void putExists() {\n        var putMethodExists = Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())\n                .anyMatch(m -> m.getName().equals(\"put\"));\n\n        assertTrue(putMethodExists);\n    }\n\n    @Test\n    @Order(5)\n    @DisplayName(\"put method declares one type parameter T\")\n    void putDeclaresOneTypeParam() {\n        var putMethod = getPutMethod();\n\n        var methodTypeParameters = putMethod.getTypeParameters();\n        assertThat(methodTypeParameters).hasSize(1);\n\n        var typeParam = putMethod.getTypeParameters()[0];\n        assertThat(typeParam.getName()).isEqualTo(\"T\");\n    }\n\n    @Test\n    @Order(6)\n    @DisplayName(\"put method accepts type-safe key\")\n    void putMethodAcceptsTypeSafeKeyParameter() {\n        var putMethod = getPutMethod();\n\n        var typeParam = (ParameterizedType) putMethod.getGenericParameterTypes()[0];\n        var typeArgument = typeParam.getActualTypeArguments()[0];\n\n        assertThat(typeParam.getRawType()).isEqualTo(Class.class);\n        assertThat(typeArgument.getTypeName()).isEqualTo(\"T\");\n    }\n\n    @Test\n    @Order(7)\n    @DisplayName(\"put method accepts comparable value\")\n    void putMethodAcceptsComparableValueParameter() {\n        var putMethod = getPutMethod();\n\n        var typeParam = putMethod.getTypeParameters()[0];\n        var boundType = (ParameterizedType) typeParam.getBounds()[0];\n\n        assertThat(boundType.getRawType()).isEqualTo(Comparable.class);\n    }\n\n    @Test\n    @Order(8)\n    @DisplayName(\"put method supports value that has comparable super class\")\n    void putMethodAcceptsValueParameterWithComparableSuperClass() {\n        var putMethod = getPutMethod();\n\n        var typeParam = putMethod.getTypeParameters()[0];\n        var boundType = (ParameterizedType) typeParam.getBounds()[0];\n        var typeArgument = boundType.getActualTypeArguments()[0].getTypeName();\n\n        assertThat(boundType.getRawType()).isEqualTo(Comparable.class);\n        assertThat(typeArgument).isEqualTo(\"? super T\");\n    }\n\n    @Test\n    @Order(9)\n    @SneakyThrows\n    @DisplayName(\"put stores provided value when current max is null\")\n    void putStoresValueWhenCurrentMaxIsNull() {\n        callPut(String.class, \"I am maximum\");\n        var storedMaxValue = getMaxHelper(String.class);\n\n        assertThat(storedMaxValue).isEqualTo(\"I am maximum\");\n    }\n\n    @Test\n    @Order(10)\n    @SneakyThrows\n    @DisplayName(\"put returns null when current max is null\")\n    void putReturnsNullWhenCurrentMaxIsNull() {\n        var result = callPut(String.class, \"I am maximum\");\n\n        assertThat(result).isNull();\n    }\n\n    @Test\n    @Order(11)\n    @SneakyThrows\n    @DisplayName(\"put stores provided value when current max is smaller than it\")\n    void putStoresValueWhenCurrentMaxIsSmaller() {\n        givenMaxHolderWithData(Long.class, 123L);\n\n        callPut(Long.class, 222L);\n        var storedMaxValue = getMaxHelper(Long.class);\n\n        assertThat(storedMaxValue).isEqualTo(222L);\n    }\n\n    @Test\n    @Order(12)\n    @SneakyThrows\n    @DisplayName(\"put returns old max value when the provided value is greater than it\")\n    void putReturnsOldMaxValue() {\n        givenMaxHolderWithData(Long.class, 123L);\n\n        var returnedValue = callPut(Long.class, 222L);\n\n        assertThat(returnedValue).isEqualTo(123L);\n    }\n\n    @Test\n    @Order(13)\n    @SneakyThrows\n    @DisplayName(\"put ignores provided value when the current max is greater than it\")\n    void putIgnoresNewValueWhenCurrentMaxIsGreater() {\n        givenMaxHolderWithData(Long.class, 123L);\n\n        callPut(Long.class, 101L);\n        var storedMaxValue = getMaxHelper(Long.class);\n\n        assertThat(storedMaxValue).isEqualTo(123L);\n    }\n\n    @Test\n    @Order(14)\n    @SneakyThrows\n    @DisplayName(\"put returns provided value when the current max is greater than it\")\n    void putReturnsProvidedValueWhenCurrentMaxIsGreater() {\n        givenMaxHolderWithData(Long.class, 123L);\n\n        var returnedValue = callPut(Long.class, 101L);\n\n        assertThat(returnedValue).isEqualTo(101L);\n    }\n\n    @Test\n    @Order(15)\n    @SneakyThrows\n    @DisplayName(\"put method is overloaded with additional Comparator parameter\")\n    void putIsOverloadedWithAdditionalComparatorParam() {\n        HeterogeneousMaxHolder.class.getMethod(\"put\", Class.class, Object.class, Comparator.class);\n    }\n\n    @Test\n    @Order(16)\n    @DisplayName(\"Overloaded put method declares one type parameter T\")\n    void overloadedPutDeclaresOneTypeParam() {\n        var putMethod = getOverloadedPutMethod();\n\n        var methodTypeParameters = putMethod.getTypeParameters();\n        assertThat(methodTypeParameters).hasSize(1);\n\n        var typeParam = putMethod.getTypeParameters()[0];\n        assertThat(typeParam.getName()).isEqualTo(\"T\");\n    }\n\n    @Test\n    @Order(17)\n    @DisplayName(\"Overloaded put method accepts type-safe key\")\n    void overloadedPutMethodAcceptsTypeSafeKeyParameter() {\n        var putMethod = getOverloadedPutMethod();\n\n        var typeParam = (ParameterizedType) putMethod.getGenericParameterTypes()[0];\n        var typeArgument = typeParam.getActualTypeArguments()[0];\n\n        assertThat(typeParam.getRawType()).isEqualTo(Class.class);\n        assertThat(typeArgument.getTypeName()).isEqualTo(\"T\");\n    }\n\n    @Test\n    @Order(18)\n    @DisplayName(\"Overloaded put method accepts value of arbitrary type T\")\n    void overloadedPutMethodAcceptsAnyValue() {\n        var putMethod = getOverloadedPutMethod();\n\n        var genericValueTypeParam = putMethod.getGenericParameterTypes()[1];\n        var actualValueTypeParm = putMethod.getParameterTypes()[1];\n\n        assertThat(genericValueTypeParam.getTypeName()).isEqualTo(\"T\");\n        assertThat(actualValueTypeParm).isEqualTo(Object.class);\n    }\n\n    @Test\n    @Order(19)\n    @SneakyThrows\n    @DisplayName(\"Overloaded put method supports comparator of a super type\")\n    void overloadedPutAcceptsComparatorOfSuperTypes() {\n        var putMethod = HeterogeneousMaxHolder.class.getMethod(\"put\", Class.class, Object.class, Comparator.class);\n\n        var comparatorParam = (ParameterizedType) putMethod.getGenericParameterTypes()[2];\n        var comparatorTypeArgument = comparatorParam.getActualTypeArguments()[0];\n\n        assertThat(comparatorTypeArgument.getTypeName()).isEqualTo(\"? super T\");\n    }\n\n    @Test\n    @Order(20)\n    @SneakyThrows\n    @DisplayName(\"Overloaded put stores provided value when current max is null\")\n    void overloadedPutStoresValueWhenCurrentMaxIsNull() {\n        var account = Accounts.generateAccount();\n        callPut(Account.class, account, Comparator.comparing(Account::getBalance));\n        var storedMaxValue = getMaxHelper(Account.class);\n\n        assertThat(storedMaxValue).isEqualTo(account);\n    }\n\n    @Test\n    @Order(21)\n    @SneakyThrows\n    @DisplayName(\"Overloaded put returns null when current max is null\")\n    void overloadedPutReturnsNullWhenCurrentMaxIsNull() {\n        var result = callPut(Account.class, Accounts.generateAccount(), Comparator.comparing(Account::getBalance));\n\n        assertThat(result).isNull();\n    }\n\n    @Test\n    @Order(22)\n    @SneakyThrows\n    @DisplayName(\"Overloaded put stores provided value when current max is smaller than it\")\n    void overloadedPutStoresValueWhenCurrentMaxIsSmaller() {\n        var givenAccount = Accounts.generateAccount();\n        givenMaxHolderWithData(Account.class, givenAccount);\n        var biggerBalanceAccount = Accounts.generateAccount();\n        biggerBalanceAccount.setBalance(givenAccount.getBalance().add(BigDecimal.TEN));\n\n        callPut(Account.class, biggerBalanceAccount, Comparator.comparing(Account::getBalance));\n        var storedMaxValue = getMaxHelper(Account.class);\n\n        assertThat(storedMaxValue).isEqualTo(biggerBalanceAccount);\n    }\n\n    @Test\n    @Order(23)\n    @SneakyThrows\n    @DisplayName(\"Overloaded put returns old max value when the provided value is greater than it\")\n    void overloadedPutReturnsOldMaxValue() {\n        var givenAccount = Accounts.generateAccount();\n        givenMaxHolderWithData(Account.class, givenAccount);\n        var biggerBalanceAccount = Accounts.generateAccount();\n        biggerBalanceAccount.setBalance(givenAccount.getBalance().add(BigDecimal.TEN));\n\n        var returnedValue = callPut(Account.class, biggerBalanceAccount, Comparator.comparing(Account::getBalance));\n\n        assertThat(returnedValue).isEqualTo(givenAccount);\n    }\n\n    @Test\n    @Order(24)\n    @SneakyThrows\n    @DisplayName(\"Overloaded put ignores provided value when the current max is greater than it\")\n    void overloadedPutIgnoresNewValueWhenCurrentMaxIsGreater() {\n        var givenAccount = Accounts.generateAccount();\n        givenMaxHolderWithData(Account.class, givenAccount);\n        var smallerBalanceAccount = Accounts.generateAccount();\n        smallerBalanceAccount.setBalance(givenAccount.getBalance().subtract(BigDecimal.TEN));\n\n        callPut(Account.class, smallerBalanceAccount, Comparator.comparing(Account::getBalance));\n        var storedMaxValue = getMaxHelper(Account.class);\n\n        assertThat(storedMaxValue).isEqualTo(givenAccount);\n    }\n\n    @Test\n    @Order(25)\n    @SneakyThrows\n    @DisplayName(\"Overloaded put returns provided value when the current max is greater\")\n    void overloadedPutReturnsProvidedValueWhenCurrentMaxIsGreater() {\n        var givenAccount = Accounts.generateAccount();\n        givenMaxHolderWithData(Account.class, givenAccount);\n        var smallerBalanceAccount = Accounts.generateAccount();\n        smallerBalanceAccount.setBalance(givenAccount.getBalance().subtract(BigDecimal.TEN));\n\n        var returnedValue = callPut(Account.class, smallerBalanceAccount, Comparator.comparing(Account::getBalance));\n\n        assertThat(returnedValue).isEqualTo(smallerBalanceAccount);\n    }\n\n    @Test\n    @Order(26)\n    @DisplayName(\"getMax method exists\")\n    void getMaxExists() {\n        var getMaxMethodExists = Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())\n                .anyMatch(m -> m.getName().equals(\"getMax\"));\n\n        assertThat(getMaxMethodExists).isTrue();\n    }\n\n    @Test\n    @Order(27)\n    @DisplayName(\"getMax declares one simple type param 'T'\")\n    void getMaxDeclaresOneTypeParam() {\n        var getMaxMethod = getGetMaxMethod();\n\n        var typeParams = getMaxMethod.getTypeParameters();\n\n        assertThat(typeParams).hasSize(1);\n        assertThat(typeParams[0].getTypeName()).isEqualTo(\"T\");\n    }\n\n    @Test\n    @Order(28)\n    @DisplayName(\"getMax has one parameter\")\n    void getMaxHasOneParameter() {\n        var getMaxMethod = getGetMaxMethod();\n\n        var parameters = getMaxMethod.getParameters();\n\n        assertThat(parameters).hasSize(1);\n        assertThat(parameters[0].getType()).isEqualTo(Class.class);\n    }\n\n    @Test\n    @Order(29)\n    @DisplayName(\"getMax param specifies type arguments\")\n    void getMaxParamSpecifyTypeArguments() {\n        var getMaxMethod = getGetMaxMethod();\n\n        var parameter = getMaxMethod.getParameters()[0];\n\n        assertThat(parameter.getParameterizedType().getTypeName()).isEqualTo(Class.class.getTypeName() + \"<T>\");\n    }\n\n    @Test\n    @Order(30)\n    @DisplayName(\"getMax returns value when it exists\")\n    void getMaxReturnsValueWhenItExists() {\n        givenMaxHolderWithData(String.class, \"I am maximum\");\n\n        var returnedValue = callGetMax(String.class);\n\n        assertThat(returnedValue).isEqualTo(\"I am maximum\");\n    }\n\n    @Test\n    @Order(31)\n    @DisplayName(\"getMax returns value when it exists\")\n    void getMaxReturnsNullWhenNoValueByGivenTypeExists() {\n        var returnedValue = callGetMax(String.class);\n\n        assertThat(returnedValue).isNull();\n    }\n\n    @Test\n    @Order(32)\n    @DisplayName(\"HeterogeneousMaxHolder keeps track of value one per each type\")\n    void maxHolderKeepsTrackOfMultipleValuesPerType() {\n        callPut(String.class, \"A\");\n        callPut(String.class, \"C\");\n        callPut(String.class, \"B\");\n        callPut(Long.class, 1L);\n        callPut(Long.class, 5L);\n        callPut(Long.class, 25L);\n\n        var stringMax = callGetMax(String.class);\n        var longMax = callGetMax(Long.class);\n\n        assertThat(stringMax).isEqualTo(\"C\");\n        assertThat(longMax).isEqualTo(25L);\n    }\n\n    @SneakyThrows\n    private <T> T callGetMax(Class<T> type) {\n        var getMaxMethod = getGetMaxMethod();\n        var result = getMaxMethod.invoke(heterogeneousMaxHolder, type);\n        return type.cast(result);\n    }\n\n    private Method getGetMaxMethod() {\n        return Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())\n                .filter(m -> m.getName().equals(\"getMax\"))\n                .findAny()\n                .orElseThrow();\n    }\n\n    private Method getPutMethod() {\n        return Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())\n                .filter(m -> m.getName().equals(\"put\"))\n                .filter(m -> m.getParameters().length <= 2)\n                .findAny()\n                .orElseThrow();\n    }\n\n    private Method getOverloadedPutMethod() {\n        return Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())\n                .filter(m -> m.getName().equals(\"put\"))\n                .filter(m -> m.getParameters().length == 3)\n                .findAny()\n                .orElseThrow();\n    }\n\n    private void givenMaxHolderWithData(Class<?> type, Object value) {\n        var map = getInternalMap();\n        map.put(type, value);\n        assertThat(getMaxHelper(type)).isEqualTo(value);\n    }\n\n    @SneakyThrows\n    private <T> T callPut(Class<T> type, T value) {\n        var putMethod = HeterogeneousMaxHolder.class.getMethod(\"put\", Class.class, Comparable.class);\n\n        return type.cast(putMethod.invoke(heterogeneousMaxHolder, type, value));\n    }\n\n    @SneakyThrows\n    private <T> T callPut(Class<T> type, T value, Comparator<T> comparator) {\n        var putMethod = HeterogeneousMaxHolder.class.getMethod(\"put\", Class.class, Object.class, Comparator.class);\n\n        return type.cast(putMethod.invoke(heterogeneousMaxHolder, type, value, comparator));\n    }\n\n    @SneakyThrows\n    private <T> T getMaxHelper(Class<T> type) {\n        Map<Class<?>, Object> map = getInternalMap();\n        return type.cast(map.get(type));\n    }\n\n    @SneakyThrows\n    private Map<Class<?>, Object> getInternalMap() {\n        var mapField = HeterogeneousMaxHolder.class.getDeclaredFields()[0];\n        mapField.setAccessible(true);\n        @SuppressWarnings(\"unchecked\")\n        var map = (Map<Class<?>, Object>) mapField.get(heterogeneousMaxHolder);\n        return map;\n    }\n}"
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Hello Annotations\nLearn annotations basics to better understand how the frameworks use them 💪\n\n### Objectives\n\n* **create a custom annotation** ✅\n* specify **where it can be used** ([`@Target`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Target.html)) ✅\n* specify **where its information can be accessed** ([`@Retention`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Retention.html)) ✅\n* add **annotation members** (methods that act like fields) ✅\n* set **default value** for a member ✅\n* **use custom annotation** on a class ✅\n\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/KF1H2EOCdD4/0.jpg)](https://www.youtube.com/watch?v=KF1H2EOCdD4)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>1-0-java-basics</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>1-5-0-hello-annotations</artifactId>\n\n</project>"
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java",
    "content": "package com.bobocode.basics;\n\n/**\n * {@link HelloAnnotationsExercise} is an exercise class that is marked with be corresponding @{@link Exercise}\n * annotation. The annotation value specifies exercise name \"hello-annotation-basic\". It does not specify any custom\n * complexity level, because this exercise is a basic, which correspond to the default value provided by annotation.\n * <p>\n * todo: Create an annotation @{@link Exercise}.\n * todo: Set its retention policy so it is visible at runtime\n * todo: Set its target so it can only be applied to a class\n * todo: Add String value that will store exercise name\n * todo: Add complexityLevel with a default {@link Level} basic\n *\n * @author Taras Boychuk\n */\npublic class HelloAnnotationsExercise { // todo: mark class with the annotation according to the javadoc\n}\n"
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/Level.java",
    "content": "package com.bobocode.basics;\n\n/**\n * Enum that lists all possible exercise complexity levels.\n */\npublic enum Level {\n    BEGINNER, BASIC, ADVANCED, CRAZY\n}\n"
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsExerciseTest.java",
    "content": "package com.bobocode.basics;\n\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.*;\n\nimport java.lang.annotation.*;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class HelloAnnotationsExerciseTest {\n\n    @Test\n    @Order(1)\n    @DisplayName(\"Annotation @Exercise exists\")\n    void exerciseAnnotationExists() {\n        assertThatCode(this::getExerciseAnnotation).doesNotThrowAnyException();\n    }\n\n    @Test\n    @Order(2)\n    @DisplayName(\"@Exercise can be applied to classes and interfaces but not to methods & fields\")\n    @SneakyThrows\n    void exerciseAnnotationCanBeAppliedForClasses() {\n        var exerciseAnnotation = getExerciseAnnotation();\n\n        var target = exerciseAnnotation.getAnnotation(Target.class);\n\n        assertThat(target.value()).hasSize(1);\n        assertThat(target.value()[0]).isEqualTo(ElementType.TYPE);\n    }\n\n    @Test\n    @Order(3)\n    @DisplayName(\"@Exercise information is accessible at runtime\")\n    @SneakyThrows\n    void exerciseAnnotationInfoIsAccessibleAtRuntime() {\n        var exerciseAnnotation = getExerciseAnnotation();\n\n        var retention = exerciseAnnotation.getAnnotation(Retention.class);\n\n        assertThat(retention.value()).isEqualTo(RetentionPolicy.RUNTIME);\n    }\n\n    @Test\n    @Order(4)\n    @DisplayName(\"@Exercise has declared value\")\n    @SneakyThrows\n    void exerciseAnnotationHasValueDeclared() {\n        var exerciseAnnotation = getExerciseAnnotation();\n\n        assertThatCode(() -> exerciseAnnotation.getDeclaredMethod(\"value\"))\n                .doesNotThrowAnyException();\n    }\n\n    @Test\n    @Order(4)\n    @DisplayName(\"@Exercise has complexityLevel declared\")\n    @SneakyThrows\n    void exerciseAnnotationHasComplexityDeclared() {\n        var exerciseAnnotation = getExerciseAnnotation();\n\n        assertThatCode(() -> exerciseAnnotation.getDeclaredMethod(\"complexityLevel\"))\n                .doesNotThrowAnyException();\n    }\n\n    @Test\n    @Order(5)\n    @DisplayName(\"@Exercise complexityLevel is BASIC by default\")\n    @SneakyThrows\n    void exerciseAnnotationComplexityLevelDefaultValue() {\n        var exerciseAnnotation = getExerciseAnnotation();\n\n        var complexityLevel = exerciseAnnotation.getDeclaredMethod(\"complexityLevel\");\n\n        assertThat(complexityLevel.getDefaultValue()).isEqualTo(Level.BASIC);\n    }\n\n    @Test\n    @Order(6)\n    @DisplayName(\"HelloAnnotationExercise is marked as @Exercise with name \\\"hello-annotation-basic\\\"\")\n    @SneakyThrows\n    void helloAnnotationExerciseIsAnnotatedWithExercise() {\n        var exerciseAnnotationClass = getExerciseAnnotation();\n        var basicExerciseAnnotation = HelloAnnotationsExercise.class.getAnnotation(exerciseAnnotationClass);\n\n        var valueMethod = exerciseAnnotationClass.getMethod(\"value\");\n        var exerciseName = valueMethod.invoke(basicExerciseAnnotation);\n\n        assertThat(exerciseName).isEqualTo(\"hello-annotation-basic\");\n    }\n\n    @SneakyThrows\n    private Class<? extends Annotation> getExerciseAnnotation() {\n        return Class.forName(\"com.bobocode.basics.Exercise\")\n                .asSubclass(Annotation.class);\n    }\n\n}\n"
  },
  {
    "path": "1-0-java-basics/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Java Basics\nThis module intentionally left empty and will be populated with new material in future"
  },
  {
    "path": "1-0-java-basics/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>pom</packaging>\n    <modules>\n        <module>1-3-0-hello-generics</module>\n        <module>1-3-1-crazy-generics</module>\n        <module>1-3-2-heterogeneous-max-holder</module>\n        <module>1-5-0-hello-annotations</module>\n    </modules>\n\n    <parent>\n        <groupId>com.bobocode</groupId>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <artifactId>1-0-java-basics</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>java-fundamentals-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n            <scope>compile</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Node exercise 💪\n\nBuild strong skill of creating and linking **nodes – building blocks** that are used in order to create **LinkedList**, **LinkedQueue** and other\nimportant data structures 💪\n\n### Pre-conditions ❗\nYou're supposed to be familiar **Java classes** and **generics**\n\n### Objectives\n* implement a generic class `Node<T>` ✅\n* **link** two node objects ✅\n* create a **list of linked nodes** ✅\n* create a **circle of linked nodes** ✅\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/Ot5ma8NXcS0/0.jpg)](https://www.youtube.com/watch?v=Ot5ma8NXcS0)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>2-0-data-structures-and-algorithms</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>2-2-1-node</artifactId>\n\n\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/src/main/java/com/bobobode/cs/Node.java",
    "content": "package com.bobobode.cs;\n\n/**\n * Class {@link Node} is a very simple data structure that consists of an element itself and the reference to the next\n * node. An element can have any value since it's a generic. A reference to the next node allows to link {@link Node}\n * objects and build more comprehensive data structures on top of those liked nodes.\n *\n * @param <T> a generic type T\n * @author Taras Boychuk\n */\npublic class Node<T> {\n    // todo:\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/src/main/java/com/bobobode/cs/Nodes.java",
    "content": "package com.bobobode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * A class that consists of static methods only and provides util methods for {@link Node}.\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @author Taras Boychuk\n */\npublic class Nodes {\n    private Nodes() {\n    }\n\n    /**\n     * Creates a new instance of {@link Node} that holds provided element\n     *\n     * @param element any element of type T\n     * @param <T>     generic type\n     * @return a new instance of {@link Node}\n     */\n    public static <T> Node<T> create(T element) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Create a connection between first and second nodes, so object first stores a reference to the second.\n     *\n     * @param first  any {@link Node} object\n     * @param second any {@link Node} object\n     * @param <T>    a genetic type\n     */\n    public static <T> void link(Node<T> first, Node<T> second) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Creates two new {@link Node} objects using provided firstElement and secondElement, and create a connection\n     * between those two elements so the first node will hold a reference to a second one.\n     *\n     * @param firstElement  any element of type T\n     * @param secondElement any element of type T\n     * @param <T>           a genetic type\n     * @return a reference to a first node created based on firstElement\n     */\n    public static <T> Node<T> pairOf(T firstElement, T secondElement) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Creates two new {@link Node} objects using provided firstElement and secondElement, and creates connections\n     * between those nodes so the first node will hold a reference to a second one, and a second node will hold\n     * a reference to the first one.\n     *\n     * @param firstElement  any element of type T\n     * @param secondElement any element of type T\n     * @param <T>           generic type T\n     * @return a reference to the first node\n     */\n    public static <T> Node<T> closedPairOf(T firstElement, T secondElement) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Creates a linked chain of {@link Node} objects based on provided elements. Creates a connection between those\n     * nodes so each node will hold a reference to the next one in the chain. HINT: it's basically a linked list.\n     *\n     * @param elements a array of elements of type T\n     * @param <T>      generic type T\n     * @return a reference to the first element of the chain\n     */\n    public static <T> Node<T> chainOf(T... elements) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Creates a linked circle of {@link Node} objects based on provided elements. Creates a connection between those\n     * nodes so each node will hold a reference to the next one in the chain, and the last one will hold a reference to\n     * the first one.\n     *\n     * @param elements a array of elements of type T\n     * @param <T>      generic type T\n     * @return a reference to the first element of the chain\n     */\n    public static <T> Node<T> circleOf(T... elements) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/src/test/java/com/bobocode/cs/NodesTest.java",
    "content": "package com.bobocode.cs;\n\nimport com.bobobode.cs.Node;\nimport com.bobobode.cs.Nodes;\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\nclass NodesTest {\n\n    @Test\n    @Order(1)\n    void create() {\n        int element = 5;\n\n        Node<Integer> node = Nodes.create(element);\n\n        assertThat(getNodeElement(node)).isEqualTo(element);\n        assertThat(getNodeNext(node)).isNull();\n    }\n\n    @Test\n    @Order(2)\n    void link() {\n        Node<Integer> firstNode = createNodeOf(5);\n        Node<Integer> secondNode = createNodeOf(9);\n        Node<Integer> thirdNode = createNodeOf(100);\n        setNodeNext(secondNode, thirdNode);\n\n        Nodes.link(firstNode, secondNode);\n\n        assertThat(getNodeNext(firstNode)).isEqualTo(secondNode);\n        assertThat(getNodeNext(secondNode)).isEqualTo(thirdNode);\n    }\n\n    @Test\n    @Order(3)\n    void pair() {\n        int firstElement = 8;\n        int secondElement = 2;\n\n        Node<Integer> firstNode = Nodes.pairOf(firstElement, secondElement);\n\n        Node<Integer> secondNode = getNodeNext(firstNode);\n        assertThat(getNodeElement(firstNode)).isEqualTo(firstElement);\n        assertThat(getNodeElement(secondNode)).isEqualTo(secondElement);\n        assertThat(getNodeNext(secondNode)).isNull();\n    }\n\n    @Test\n    @Order(4)\n    void closedPair() {\n        int firstElement = 8;\n        int secondElement = 2;\n\n        Node<Integer> firstNode = Nodes.closedPairOf(firstElement, secondElement);\n\n        Node<Integer> secondNode = getNodeNext(firstNode);\n        assertThat(getNodeElement(firstNode)).isEqualTo(firstElement);\n        assertThat(getNodeElement(secondNode)).isEqualTo(secondElement);\n        assertThat(getNodeNext(secondNode)).isEqualTo(firstNode);\n    }\n\n    @Test\n    @Order(5)\n    void chain() {\n        int firstElement = 8;\n        int secondElement = 1;\n        int thirdElement = 13;\n        int fourthElement = 5;\n\n        Node<Integer> firstNode = Nodes.chainOf(firstElement, secondElement, thirdElement, fourthElement);\n\n        Node<Integer> secondNode = getNodeNext(firstNode);\n        Node<Integer> thirdNode = getNodeNext(secondNode);\n        Node<Integer> fourthNode = getNodeNext(thirdNode);\n        assertThat(getNodeElement(firstNode)).isEqualTo(firstElement);\n        assertThat(getNodeElement(secondNode)).isEqualTo(secondElement);\n        assertThat(getNodeElement(thirdNode)).isEqualTo(thirdElement);\n        assertThat(getNodeElement(fourthNode)).isEqualTo(fourthElement);\n        assertThat(getNodeNext(fourthNode)).isNull();\n    }\n\n    @Test\n    @Order(6)\n    void circle() {\n        int firstElement = 8;\n        int secondElement = 1;\n        int thirdElement = 13;\n        int fourthElement = 5;\n\n        Node<Integer> firstNode = Nodes.circleOf(firstElement, secondElement, thirdElement, fourthElement);\n\n        Node<Integer> secondNode = getNodeNext(firstNode);\n        Node<Integer> thirdNode = getNodeNext(secondNode);\n        Node<Integer> fourthNode = getNodeNext(thirdNode);\n        assertThat(getNodeElement(firstNode)).isEqualTo(firstElement);\n        assertThat(getNodeElement(secondNode)).isEqualTo(secondElement);\n        assertThat(getNodeElement(thirdNode)).isEqualTo(thirdElement);\n        assertThat(getNodeElement(fourthNode)).isEqualTo(fourthElement);\n        assertThat(getNodeNext(fourthNode)).isEqualTo(firstNode);\n    }\n\n    @SneakyThrows\n    @SuppressWarnings(\"unchecked\")\n    private Node<Integer> createNodeOf(int element) {\n        Constructor<?> constructor = Arrays.stream(Node.class.getDeclaredConstructors())\n                .findAny()\n                .orElseThrow();\n        constructor.setAccessible(true);\n        Node<Integer> node;\n        if (constructor.getParameters().length > 0) {\n            node = (Node<Integer>) constructor.newInstance(element);\n        } else {\n            node = (Node<Integer>) constructor.newInstance();\n            setNodeElement(node, element);\n        }\n        return node;\n    }\n\n    @SneakyThrows\n    @SuppressWarnings(\"unchecked\")\n    private <T> T getNodeElement(Node<T> node) {\n        Field elementField = getAccessibleElementField();\n        return (T) elementField.get(node);\n    }\n\n    @SneakyThrows\n    private <T> void setNodeElement(Node<T> node, T element) {\n        Field elementField = getAccessibleElementField();\n        elementField.set(node, element);\n    }\n\n    @SneakyThrows\n    @SuppressWarnings(\"unchecked\")\n    private <T> Node<T> getNodeNext(Node<T> node) {\n        Field nextField = getAccessibleNextField();\n        return (Node<T>) nextField.get(node);\n    }\n\n    @SneakyThrows\n    private <T> void setNodeNext(Node<T> node, Node<T> next) {\n        Field elementField = getAccessibleNextField();\n        elementField.set(node, next);\n    }\n\n    private Field getAccessibleElementField() {\n        Field elementField = Arrays.stream(Node.class.getDeclaredFields())\n                .filter(field -> field.getType().equals(Object.class))\n                .findAny()\n                .orElseThrow();\n        elementField.setAccessible(true);\n        return elementField;\n    }\n\n    private Field getAccessibleNextField() {\n        Field nextField = Arrays.stream(Node.class.getDeclaredFields())\n                .filter(field -> field.getType().equals(Node.class))\n                .findAny()\n                .orElseThrow();\n        nextField.setAccessible(true);\n        return nextField;\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Stack 🥞\nLearn the Stack data structure and gain deep understanding implementing it on your own 💪\n\n\n### WHY ❓\n[Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) is one of the most important data structures \nfor software developers. It can be used in various algorithms, but the most important is that **JVM creates a stack \nfor each thread.** 😯 Even if you don't know anything about concurrency, and you write a simple application, it is still\n**executed by one main thread.** So each thread has its own stack, and **that stack is used to store method frames.** 😲\nA [method frame](https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-2.html#jvms-2.6) is just a term. It simply\nmeans all the information we operate with, when executing a method. E.g. **when you pass some arguments, or create \nlocal variable, all that data is stored to the stack.** So, **every a method is called, a new stack frame is created and\nstored to the stack.** \n\n> It is important to understand Stack, because otherwise you won't be able to understand fundamental things like **how JVM executes methods** and **how it uses memory**.\n\n### Objectives\n* implement a generic class `Node<T>` ✅\n* **push an element onto the stack** ✅\n* **get an element from the stack** ✅\n* maintain stack **size** ✅\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction) \n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>2-0-data-structures-and-algorithms</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>2-2-2-stack</artifactId>\n\n\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/LinkedStack.java",
    "content": "package com.bobocode.cs;\n\nimport com.bobocode.cs.exception.EmptyStackException;\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link LinkedStack} is a stack implementation that is based on singly linked generic nodes.\n * A node is implemented as inner static class {@link Node<T>}.\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @param <T> generic type parameter\n * @author Taras Boychuk\n * @author Serhii Hryhus\n */\npublic class LinkedStack<T> implements Stack<T> {\n\n    /**\n     * This method creates a stack of provided elements\n     *\n     * @param elements elements to add\n     * @param <T>      generic type\n     * @return a new stack of elements that were passed as method parameters\n     */\n    public static <T> LinkedStack<T> of(T... elements) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * The method pushes an element onto the top of this stack. This has exactly the same effect as:\n     * addElement(item)\n     *\n     * @param element elements to add\n     */\n    @Override\n    public void push(T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * This method removes the object at the top of this stack\n     * and returns that object as the value of this function.\n     *\n     * @return The object at the top of this stack\n     * @throws EmptyStackException - if this stack is empty\n     */\n    @Override\n    public T pop() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the number of elements in the stack\n     *\n     * @return number of elements\n     */\n    @Override\n    public int size() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Checks if a stack is empty\n     *\n     * @return {@code true} if a stack is empty, {@code false} otherwise\n     */\n    @Override\n    public boolean isEmpty() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method;\n    }\n\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/Stack.java",
    "content": "package com.bobocode.cs;\n\n/**\n * {@link Stack} is a fundamental data structure that follows last-in-first-out (LIFO) principle. This interface\n * represents a simple contact, that can be implemented in various ways (e.g. using existing collections, arrays or\n * custom linked nodes)\n *\n * @param <T> type parameter\n * @author Taras Boychuk\n * @author Serhii Hryhus\n */\npublic interface Stack<T> {\n\n    void push(T element);\n\n    T pop();\n\n    int size();\n\n    boolean isEmpty();\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/exception/EmptyStackException.java",
    "content": "package com.bobocode.cs.exception;\n\npublic class EmptyStackException extends RuntimeException{\n\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/src/test/java/com/bobocode/cs/LinkedStackTest.java",
    "content": "package com.bobocode.cs;\n\nimport com.bobocode.cs.exception.EmptyStackException;\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.*;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.function.Predicate;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatNullPointerException;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\n/**\n * A reflection-based test class for {@link LinkedStack}.\n * <p>\n * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.\n *\n * @author Ivan Virchenko\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\nclass LinkedStackTest {\n    private static final String PROPER_CLASSNAME = \"Node\";\n\n    private static final Predicate<Field> NODE_FIELD_PREDICATE = field ->\n            field.getType().getSimpleName().equals(PROPER_CLASSNAME)\n            && field.getName().toLowerCase().contains(\"head\")\n            || field.getName().toLowerCase().contains(\"first\");\n\n    private static final Predicate<Field> SIZE_FIELD_PREDICATE = field ->\n            field.getName().toLowerCase().contains(\"size\");\n\n    private static final Predicate<Field> NODE_ELEMENT_FIELD_PREDICATE = field ->\n            field.getName().toLowerCase().contains(\"element\")\n            || field.getName().toLowerCase().contains(\"value\")\n            || field.getName().toLowerCase().contains(\"item\");\n\n    private static final Predicate<Field> NODE_NEXT_FIELD_PREDICATE = field ->\n            field.getType().getSimpleName().equals(PROPER_CLASSNAME)\n            && field.getName().toLowerCase().contains(\"next\");\n\n    private Stack<Integer> intStack = new LinkedStack<>();\n\n    @Test\n    @Order(1)\n    @DisplayName(\"Inner class Node is created\")\n    void checkProperInnerClassName() {\n        String name = getInnerClass().getSimpleName();\n        assertThat(name).isEqualTo(PROPER_CLASSNAME);\n    }\n\n    @Test\n    @Order(2)\n    @DisplayName(\"Class Node is a generic class\")\n    void noteIsAGenericClass() {\n        var nodeTypeParams = getInnerClass().getTypeParameters();\n\n        assertThat(nodeTypeParams).hasSize(1);\n    }\n\n    @Test\n    @Order(3)\n    @DisplayName(\"LinkedStack class has a field that stores a reference to the first(head) element\")\n    void checkProperHeadFieldName() {\n        Field[] fields = LinkedStack.class.getDeclaredFields();\n\n        boolean hasNodeField = Arrays.stream(fields)\n                .anyMatch(NODE_FIELD_PREDICATE);\n\n        assertThat(hasNodeField).isTrue();\n    }\n\n    @Test\n    @Order(4)\n    @DisplayName(\"LinkedStack class has a field to store stack size\")\n    void checkProperSizeFieldName() {\n        Field[] fields = LinkedStack.class.getDeclaredFields();\n\n        boolean hasSizeField = Arrays.stream(fields)\n                .anyMatch(SIZE_FIELD_PREDICATE);\n\n        assertThat(hasSizeField).isTrue();\n    }\n\n    @Test\n    @Order(5)\n    @DisplayName(\"Node class has a field to store a generic element\")\n    void checkProperElementField() {\n        var fields = getInnerClass().getDeclaredFields();\n\n        var elementField = Arrays.stream(fields)\n                .filter(NODE_ELEMENT_FIELD_PREDICATE)\n                .findAny()\n                .orElseThrow();\n        var nodeTypeParameter = getInnerClass().getTypeParameters()[0];\n\n\n        assertThat(elementField.getGenericType().getTypeName()).isEqualTo(nodeTypeParameter.getTypeName());\n    }\n\n    @Test\n    @Order(6)\n    @DisplayName(\"Node class has a field to store a reference to the next node\")\n    void checkProperNextField() {\n        Field[] fields = getInnerClass().getDeclaredFields();\n\n        boolean hasNext = Arrays.stream(fields)\n                .anyMatch(NODE_NEXT_FIELD_PREDICATE);\n\n        assertThat(hasNext).isTrue();\n    }\n\n    @Test\n    @Order(7)\n    @DisplayName(\"Method of() creates a new LinkedStack of given elements\")\n    void of() {\n        intStack = LinkedStack.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);\n\n        for (int i = 1; i <= 10; i++) {\n            assertThat(contains(i)).isTrue();\n        }\n    }\n\n    @Test\n    @Order(8)\n    @DisplayName(\"Method push() adds new element on top of the stack\")\n    void push() {\n        intStack.push(55);\n\n        assertThat(contains(55)).isTrue();\n    }\n\n    @Test\n    @Order(9)\n    @DisplayName(\"Method push() adds new element on top of the stack\")\n    void pushAddsElementWhenStackIsEmpty() {\n        intStack.push(243);\n\n        assertThat(contains(243)).isTrue();\n    }\n\n    @Test\n    @Order(10)\n    @DisplayName(\"Method push() adds new element on top of the stack when it's empty\")\n    void pushAddsElementToHeadWhenStackIsEmpty() {\n        intStack.push(10);\n\n        Object head = getHeadObject();\n        int innerElement = getNodeElementInt(head);\n\n        assertThat(innerElement).isEqualTo(10);\n    }\n\n    @Test\n    @Order(11)\n    @DisplayName(\"Method push() adds new element on top of the stack when it's not empty\")\n    void pushAddsElementToHeadWhenStackIsNotEmpty() {\n        intStack.push(10);\n        intStack.push(15);\n        intStack.push(20);\n\n        Object head = getHeadObject();\n        int innerElement = getNodeElementInt(head);\n\n        assertThat(innerElement).isEqualTo(20);\n    }\n\n    @Test\n    @Order(12)\n    @DisplayName(\"Method push() links new element with the previous top (head) element\")\n    void pushPutsHeadToNextOfNewHead() {\n        fillTestStack(10, 15, 20);\n\n        assertThat(getNodeElementInt(getHeadObject())).isEqualTo(20);\n\n        intStack.push(30);\n\n        Object head = getHeadObject();\n        Object next = getNodeNextObject(head);\n        int nextElement = getNodeElementInt(next);\n\n        assertThat(nextElement).isEqualTo(20);\n    }\n\n    @Test\n    @Order(13)\n    @DisplayName(\"Method push() throws exception when element is null\")\n    void pushThrowsExceptionWhenElementIsNull() {\n        assertThatNullPointerException().isThrownBy(() -> intStack.push(null));\n    }\n\n    @Test\n    @Order(14)\n    @DisplayName(\"Method pop() throws exception when stack is empty\")\n    void popElementWhenStackIsEmpty() {\n        assertThrows(EmptyStackException.class, () -> intStack.pop());\n    }\n\n    @Test\n    @Order(15)\n    @DisplayName(\"Method pop() retrieves top element from the stack (LIFO)\")\n    void pop() {\n        fillTestStack(55, 17, 66, 234);\n\n        int lastElement = intStack.pop();\n\n        assertThat(lastElement).isEqualTo(234);\n    }\n\n    @Test\n    @Order(16)\n    @DisplayName(\"Method pop() assigns next element to be a head\")\n    void popResetsHeadFromNextOfOldHead() {\n        fillTestStack(10, 15, 20);\n        Object head = getHeadObject();\n\n        assertThat(getNodeElementInt(head)).isEqualTo(20);\n\n        intStack.pop();\n        head = getHeadObject();\n\n        assertThat(getNodeElementInt(head)).isEqualTo(15);\n    }\n\n    @Test\n    @Order(17)\n    @DisplayName(\"Method size() returns 0 when stack is empty\")\n    void sizeWhenStackIsEmpty() {\n        int actualSize = getInnerSize();\n\n        assertThat(actualSize).isEqualTo(0);\n    }\n\n    @Test\n    @Order(18)\n    @DisplayName(\"Method size() returns number of element in the stack\")\n    void size() {\n        fillTestStack(1, 5, 7);\n\n        assertThat(intStack.size()).isEqualTo(3);\n    }\n\n    @Test\n    @Order(19)\n    @DisplayName(\"Method size() returns correct value when stack was created via method of()\")\n    void sizeIncreasesWhenUseOfMethod() {\n        intStack = LinkedStack.of(1, 2, 3, 4, 5, 6, 7, 8);\n\n        assertThat(intStack.size()).isEqualTo(8);\n    }\n\n    @Test\n    @Order(20)\n    @DisplayName(\"Method size() correct value when elements were added via push()\")\n    void sizeIncreasesWhenPush() {\n        intStack.push(1);\n        intStack.push(2);\n        intStack.push(3);\n\n        assertThat(intStack.size()).isEqualTo(3);\n    }\n\n    @Test\n    @Order(21)\n    @DisplayName(\"Method size() correct value when elements were removed via pop()\")\n    void sizeDecreasesWhenPop() {\n        fillTestStack(1, 2, 3, 4, 5);\n        intStack.pop();\n\n        assertThat(intStack.size()).isEqualTo(4);\n    }\n\n    @Test\n    @Order(22)\n    @DisplayName(\"Method isEmpty() returns true when stack contains elements\")\n    void isEmpty() {\n        fillTestStack(87, 53, 66);\n\n        boolean stackEmpty = intStack.isEmpty();\n\n        assertThat(stackEmpty).isEqualTo(false);\n    }\n\n    @Test\n    @Order(23)\n    @DisplayName(\"Method isEmpty() returns false when stack contains no elements\")\n    void isEmptyWhenStackIsEmpty() {\n        boolean stackEmpty = intStack.isEmpty();\n        assertThat(stackEmpty).isEqualTo(true);\n    }\n\n    private Class<?> getInnerClass() {\n        return Arrays.stream(LinkedStack.class.getDeclaredClasses())\n                .filter(Class::isMemberClass)\n                .findAny().orElseThrow();\n    }\n\n    private Field getHeadField() {\n        Field headField = Arrays.stream(LinkedStack.class.getDeclaredFields())\n                .filter(NODE_FIELD_PREDICATE)\n                .findAny()\n                .orElseThrow();\n        headField.setAccessible(true);\n        return headField;\n    }\n\n    private Field getNodeElementField(Object node) {\n        Field fieldElement = Arrays.stream(node.getClass().getDeclaredFields())\n                .filter(NODE_ELEMENT_FIELD_PREDICATE)\n                .findAny()\n                .orElseThrow();\n        fieldElement.setAccessible(true);\n        return fieldElement;\n    }\n\n    private Field getNodeNextField(Object node) {\n        Field field = Arrays.stream(node.getClass().getDeclaredFields())\n                .filter(NODE_NEXT_FIELD_PREDICATE)\n                .findAny()\n                .orElseThrow();\n        field.setAccessible(true);\n        return field;\n    }\n\n    @SneakyThrows\n    private Object getHeadObject() {\n        return getHeadField().get(intStack);\n    }\n\n    @SneakyThrows\n    private int getNodeElementInt(Object node) {\n        return (int) getNodeElementField(node).get(node);\n    }\n\n    @SneakyThrows\n    private Object getNodeNextObject(Object node) {\n        return getNodeNextField(node).get(node);\n    }\n\n    private boolean contains(int element) {\n        Object head = getHeadObject();\n        if (head == null) {\n            return false;\n        }\n\n        if (getNodeNextObject(head) != null) {\n            return checkNext(head, element);\n        } else {\n            return getNodeElementInt(head) == element;\n        }\n    }\n\n    private boolean checkNext(Object node, int element) {\n        if (getNodeElementInt(node) == element) {\n            return true;\n        } else {\n            return checkNext(getNodeNextObject(node), element);\n        }\n    }\n\n    @SneakyThrows\n    private Object newNode(int element) {\n        Constructor<?> constructor = getInnerClass().getDeclaredConstructors()[0];\n        constructor.setAccessible(true);\n\n        if (constructor.getParameters().length == 1) {\n            return constructor.newInstance(element);\n        } else if (constructor.getParameters().length == 2) {\n            return constructor.newInstance(element, null);\n        } else {\n            Object node = constructor.newInstance();\n            getNodeElementField(node).set(node, element);\n            return node;\n        }\n    }\n\n    private void fillTestStack(int... elements) {\n        for (int element : elements) {\n            addToStack(element);\n        }\n        setInnerSize(elements.length);\n    }\n\n    @SneakyThrows\n    private void addToStack(int element) {\n        Object newNode = newNode(element);\n        if (getHeadObject() != null) {\n            getNodeNextField(newNode).set(newNode, getHeadObject());\n        }\n        getHeadField().set(intStack, newNode);\n    }\n\n    private Field getInnerSizeField() {\n        Field size = Arrays.stream(LinkedStack.class.getDeclaredFields())\n                .filter(SIZE_FIELD_PREDICATE)\n                .findAny()\n                .orElseThrow();\n        size.setAccessible(true);\n        return size;\n    }\n\n    @SneakyThrows\n    private void setInnerSize(int size) {\n        getInnerSizeField().set(intStack, size);\n    }\n\n    @SneakyThrows\n    private int getInnerSize() {\n        return (int) getInnerSizeField().get(intStack);\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Linked Queue\nLearn the idea of a queue and build strong skills implementing a queue based on linked nodes 💪\n \n### Pre-conditions ❗\nYou're supposed to be familiar [Queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) data structure and generics in Java\n\n### Objectives\n* implement a generic class `Node<T>` ✅\n* **add an element** to the end of the queue ✅\n* **retrieve an element** from the begging of the queue ** ✅\n* maintain queue **size** ✅\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>2-0-data-structures-and-algorithms</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>2-2-3-linked-queue</artifactId>\n\n\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/main/java/com/bobocode/cs/LinkedQueue.java",
    "content": "package com.bobocode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link LinkedQueue} implements FIFO {@link Queue}, using singly linked nodes. Nodes are stores in instances of nested\n * class Node. In order to perform operations {@link LinkedQueue#add(Object)} and {@link LinkedQueue#poll()}\n * in a constant time, it keeps to references to the head and tail of the queue.\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @param <T> a generic parameter\n * @author Taras Boychuk\n * @author Ivan Virchenko\n */\npublic class LinkedQueue<T> implements Queue<T> {\n\n    /**\n     * Adds an element to the end of the queue.\n     *\n     * @param element the element to add\n     */\n    public void add(T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Retrieves and removes queue head.\n     *\n     * @return an element that was retrieved from the head or null if queue is empty\n     */\n    public T poll() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns a size of the queue.\n     *\n     * @return an integer value that is a size of queue\n     */\n    public int size() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Checks if the queue is empty.\n     *\n     * @return {@code true} if the queue is empty, returns {@code false} if it's not\n     */\n    public boolean isEmpty() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/main/java/com/bobocode/cs/Queue.java",
    "content": "package com.bobocode.cs;\n\n/**\n * Queue is a data structure that follows \"first in, first out\" rule (FIFO). Operations {@link Queue#add(Object)} and\n * {@link Queue#poll()} are performed in constant time O(1)\n */\npublic interface Queue<T> {\n    /**\n     * Adds an element to the end of the queue.\n     *\n     * @param element the element to add\n     */\n    void add(T element);\n\n    /**\n     * Retrieves and removes queue head.\n     *\n     * @return an element that was retrieved from the head or null if queue is empty\n     */\n    T poll();\n\n    /**\n     * Returns a size of the queue.\n     *\n     * @return an integer value that is a size of queue\n     */\n    int size();\n\n    /**\n     * Checks if the queue is empty.\n     *\n     * @return {@code true} if the queue is empty, returns {@code false} if it's not\n     */\n    boolean isEmpty();\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/test/java/com/bobocode/cs/LinkedQueueTest.java",
    "content": "package com.bobocode.cs;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.Arrays;\nimport java.util.function.Predicate;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * A reflection-based test class for {@link LinkedQueue}.\n * <p>\n * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.\n *\n * @author Victor Kuzma\n * @author Taras Boychuk\n * @author Ivan Virchenko\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class LinkedQueueTest {\n    private static final String NODE_NAME = \"Node\";\n    private static final String SIZE_NAME = \"size\";\n\n    private static final Predicate<Field> NODE_FIELD = field ->\n            field.getType().getSimpleName().equals(NODE_NAME) && (field.getName().contains(\"next\"));\n\n    private static final Predicate<Field> ELEMENT_FIELD = field ->\n            field.getGenericType().getTypeName().equals(\"T\")\n            && (field.getName().contains(\"elem\") || field.getName().contains(\"value\") || field.getName().contains(\"item\"));\n\n    private static final Predicate<Field> NEXT_FIELD = field ->\n            field.getGenericType().getTypeName().endsWith(\"Node<T>\") && (field.getName().contains(\"next\"));\n\n    private static final Predicate<Field> SIZE_FIELD = field ->\n            field.getType().getSimpleName().equals(\"int\") && (field.getName().equals(SIZE_NAME));\n\n    private static final Predicate<Field> HEAD_FIELD = field ->\n            field.getType().getSimpleName().equals(NODE_NAME)\n            && (field.getName().contains(\"head\") || field.getName().contains(\"first\"));\n\n    private static final Predicate<Field> TAIL_FIELD = field ->\n            field.getType().getSimpleName().equals(NODE_NAME)\n            && (field.getName().contains(\"tail\") || field.getName().contains(\"last\"));\n\n    private Queue<Integer> integerQueue = new LinkedQueue<>();\n\n    @Test\n    @Order(1)\n    void createNodeClass() {\n        Class<?> innerClass = getInnerStaticNodeClass();\n        String name = innerClass.getSimpleName();\n\n        assertThat(name).isEqualTo(NODE_NAME);\n    }\n\n    @Test\n    @Order(2)\n    void checkFieldsNameInNodeClass() {\n        Class<?> innerClass = getInnerStaticNodeClass();\n        boolean hasElementField = hasField(innerClass, ELEMENT_FIELD);\n        boolean hasNodeField = hasField(innerClass, NODE_FIELD);\n\n        assertThat(hasElementField).isTrue();\n        assertThat(hasNodeField).isTrue();\n    }\n\n    @Test\n    @Order(3)\n    void checkFieldsInQueueCLass() {\n        Class<?> baseClass = this.integerQueue.getClass();\n        boolean hasHeadField = hasField(baseClass, HEAD_FIELD);\n        boolean hasSizeFiled = hasField(baseClass, SIZE_FIELD);\n        boolean hasTailFiled = hasField(baseClass, TAIL_FIELD);\n\n        assertThat(hasHeadField).isTrue();\n        assertThat(hasSizeFiled).isTrue();\n        assertThat(hasTailFiled).isTrue();\n    }\n\n    @Test\n    @Order(4)\n    void addFillsQueueWhenItIsEmpty() {\n        integerQueue.add(1);\n        integerQueue.add(228);\n        int size = getInternalSize();\n        boolean isEmpty = isEmptyQueue();\n        Integer firstElement = (Integer) pollElementFromQueue();\n        Integer secondElement = (Integer) pollElementFromQueue();\n\n        assertThat(size).isEqualTo(2);\n        assertThat(isEmpty).isEqualTo(false);\n        assertThat(firstElement).isEqualTo(1);\n        assertThat(secondElement).isEqualTo(228);\n    }\n\n    @Test\n    @Order(5)\n    void addFillsQueueWhenItIsNotEmpty() {\n        addIntElementToQueue(12);\n        addIntElementToQueue(13);\n        integerQueue.add(111);\n        int size = getInternalSize();\n        boolean isEmpty = isEmptyQueue();\n        Integer firstElement = (Integer) pollElementFromQueue();\n        Integer secondElement = (Integer) pollElementFromQueue();\n        Integer tailValue = (Integer) getNodeValue(TAIL_FIELD);\n\n        assertThat(size).isEqualTo(3);\n        assertThat(isEmpty).isEqualTo(false);\n        assertThat(firstElement).isEqualTo(12);\n        assertThat(secondElement).isEqualTo(13);\n        assertThat(tailValue).isEqualTo(111);\n    }\n\n    @Test\n    @Order(6)\n    void addIncreasesQueueSize() {\n        integerQueue.add(1);\n        integerQueue.add(2);\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(2);\n    }\n\n    @Test\n    @Order(7)\n    void pollReturnsNullWhenQueueIsEmpty() {\n        Integer firstElement = this.integerQueue.poll();\n\n        assertThat(firstElement).isEqualTo(null);\n    }\n\n    @Test\n    @Order(8)\n    void pollDeletesElementFromHead() {\n        addIntElementToQueue(11);\n        addIntElementToQueue(111);\n        Integer firstElement = this.integerQueue.poll();\n        Integer secondElement = this.integerQueue.poll();\n        boolean isEmpty = isEmptyQueue();\n\n        assertThat(isEmpty).isEqualTo(true);\n        assertThat(firstElement).isEqualTo(11);\n        assertThat(secondElement).isEqualTo(111);\n    }\n\n    @Test\n    @Order(9)\n    void pollDecreasesQueueSize() {\n        addIntElementToQueue(11);\n        addIntElementToQueue(111);\n        this.integerQueue.poll();\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(1);\n    }\n\n    @Test\n    @Order(10)\n    void pollMakesSizeZeroWhenQueueHasSingleElement() {\n        addIntElementToQueue(12);\n        Integer element = this.integerQueue.poll();\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(0);\n        assertThat(element).isEqualTo(12);\n    }\n\n    @Test\n    @SneakyThrows\n    @Order(11)\n    void pollMakesQueueEmptyWhenQueueHasSingleElement() {\n        addIntElementToQueue(1);\n        this.integerQueue.poll();\n        boolean isEmpty = isEmptyQueue();\n\n        Object tail = getAccessibleFieldByPredicate(integerQueue, TAIL_FIELD).get(integerQueue);\n        Object head = getAccessibleFieldByPredicate(integerQueue, HEAD_FIELD).get(integerQueue);\n\n        assertThat(isEmpty).isEqualTo(true);\n        assertThat(tail).isNull();\n        assertThat(head).isNull();\n    }\n\n    @Test\n    @Order(12)\n    void sizeReturnsZeroWhenQueueIsEmpty() {\n        int size = this.integerQueue.size();\n\n        assertThat(size).isEqualTo(0);\n    }\n\n    @Test\n    @Order(13)\n    void size() {\n        addIntElementToQueue(1);\n        int size = this.integerQueue.size();\n\n        assertThat(size).isEqualTo(1);\n    }\n\n    @Test\n    @Order(14)\n    void isEmptyReturnsTrueWhenQueueIsEmpty() {\n        boolean isEmpty = this.integerQueue.isEmpty();\n\n        assertThat(isEmpty).isEqualTo(true);\n    }\n\n    @Test\n    @Order(15)\n    void isEmpty() {\n        addIntElementToQueue(1);\n        boolean isEmpty = integerQueue.isEmpty();\n\n        assertThat(isEmpty).isEqualTo(false);\n    }\n\n\n    private Class<?> getInnerStaticNodeClass() {\n        return Arrays.stream(integerQueue.getClass().getDeclaredClasses())\n                .filter(aClass -> Modifier.isStatic(aClass.getModifiers()))\n                .findAny()\n                .orElseThrow();\n    }\n\n    private boolean hasField(Class<?> classToSearch, Predicate targetField) {\n        return Arrays.stream(classToSearch.getDeclaredFields())\n                .anyMatch(targetField);\n    }\n\n    @SneakyThrows\n    private int getInternalSize() {\n        return (int) getAccessibleFieldByPredicate(this.integerQueue, SIZE_FIELD)\n                .get(this.integerQueue);\n    }\n\n\n    @SneakyThrows\n    private Object pollElementFromQueue() {\n        Object element;\n        Object nextElement;\n        Object tail;\n        Object head = getAccessibleFieldByPredicate(this.integerQueue,\n                HEAD_FIELD)\n                .get(this.integerQueue);\n\n        Integer size = (Integer) getAccessibleFieldByPredicate(this.integerQueue,\n                SIZE_FIELD)\n                .get(this.integerQueue);\n\n        if (head != null) {\n            element = getAccessibleFieldByPredicate(head, ELEMENT_FIELD)\n                    .get(head);\n\n            nextElement = getAccessibleFieldByPredicate(head, NODE_FIELD)\n                    .get(head);\n\n            head = nextElement;\n            setHead(head);\n\n            if (head == null) {\n                tail = null;\n                setTail(tail);\n            }\n\n            if (size != null) {\n                int tmpInt = size;\n                tmpInt--;\n                setInternalSize(tmpInt);\n            }\n\n            return element;\n        } else {\n            return null;\n        }\n    }\n\n    @SneakyThrows\n    private void addIntElementToQueue(int value) {\n        Object newNode = createNode(value);\n        Object head = getAccessibleFieldByPredicate(this.integerQueue, HEAD_FIELD).get(this.integerQueue);\n        Object tail = getAccessibleFieldByPredicate(this.integerQueue, TAIL_FIELD).get(this.integerQueue);\n        Integer size = (Integer) getAccessibleFieldByPredicate(this.integerQueue, SIZE_FIELD).get(this.integerQueue);\n\n        if (head == null) {\n            setHead(newNode);\n        } else {\n            setNextNode(tail, newNode);\n        }\n        setTail(newNode);\n\n        if (size == null) {\n            setInternalSize(1);\n        } else {\n            int tmpInt = size;\n            tmpInt++;\n            setInternalSize(tmpInt);\n        }\n    }\n\n    @SneakyThrows\n    private Object createNode(int value) {\n        Object nodeObject;\n        Class<?> innerClass = getInnerStaticNodeClass();\n        Constructor<?>[] declaredConstructors = innerClass.getDeclaredConstructors();\n        Constructor<?> constructor = declaredConstructors[0];\n        constructor.setAccessible(true);\n\n        if (constructor.getParameterTypes().length == 1) {\n            nodeObject = constructor.newInstance(value);\n        } else {\n            nodeObject = constructor.newInstance();\n            Field nodeElement = getAccessibleFieldByPredicate(nodeObject, ELEMENT_FIELD);\n            nodeElement.set(nodeObject, value);\n        }\n\n        return nodeObject;\n    }\n\n    @SneakyThrows\n    private boolean isEmptyQueue() {\n        Object head = getAccessibleFieldByPredicate(this.integerQueue,\n                HEAD_FIELD)\n                .get(this.integerQueue);\n        return head == null;\n    }\n\n    @SneakyThrows\n    private void setInternalSize(int size) {\n        Field sizeField = getAccessibleFieldByPredicate(this.integerQueue,\n                SIZE_FIELD);\n        sizeField.setInt(this.integerQueue, size);\n    }\n\n    @SneakyThrows\n    private void setHead(Object obj) {\n        Field head = getAccessibleFieldByPredicate(this.integerQueue, HEAD_FIELD);\n        head.set(this.integerQueue, obj);\n    }\n\n    @SneakyThrows\n    private void setTail(Object obj) {\n        Field tail = getAccessibleFieldByPredicate(this.integerQueue, TAIL_FIELD);\n        tail.set(this.integerQueue, obj);\n    }\n\n    @SneakyThrows\n    private void setNextNode(Object current, Object next) {\n        Field nodeNextField = getAccessibleFieldByPredicate(current, NEXT_FIELD);\n        nodeNextField.set(current, next);\n    }\n\n    private Field getAccessibleFieldByPredicate(Object object, Predicate<Field> predicate) {\n        Field field = Arrays.stream(object.getClass().getDeclaredFields())\n                .filter(predicate)\n                .findAny()\n                .orElseThrow();\n        field.setAccessible(true);\n        return field;\n    }\n\n    @SneakyThrows\n    private Object getNodeValue(Predicate<Field> predicate) {\n        Object field = getAccessibleFieldByPredicate(integerQueue, predicate).get(integerQueue);\n        final Field value = getAccessibleFieldByPredicate(field, ELEMENT_FIELD);\n        value.setAccessible(true);\n        return value.get(field);\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-4-linked-list/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Linked List\nBuild strong skills implementing a well known Linked List data structure 💪\n\n### Pre-conditions ❗\nYou're supposed to be familiar [Linked List](https://en.wikipedia.org/wiki/Linked_list) data structure and generics in Java\n\n### Objectives\n* implement a generic class `Node<T>` ✅\n* **add an element** to the end of the list in **O(1)** ✅\n* **add an element by index** (relink nodes when adding a new one inside the chain) ✅\n* **set element** by index (find the correct node by index starting from `head`) ✅\n* **remove element** by index (link prev and next nodes to get rid of the one that should be removed) ✅\n* maintain list **size** ✅\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/knhSNO3bAHo/0.jpg)](https://www.youtube.com/watch?v=knhSNO3bAHo)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-4-linked-list/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>2-0-data-structures-and-algorithms</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>2-2-4-linked-list</artifactId>\n\n    \n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>data-structures-and-algorithms-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-4-linked-list/src/main/java/com/bobocode/cs/LinkedList.java",
    "content": "package com.bobocode.cs;\n\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link LinkedList} is a list implementation that is based on singly linked generic nodes. A node is implemented as\n * inner static class {@link Node<T>}.\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @param <T> generic type parameter\n * @author Taras Boychuk\n * @author Serhii Hryhus\n */\npublic class LinkedList<T> implements List<T> {\n\n    /**\n     * This method creates a list of provided elements\n     *\n     * @param elements elements to add\n     * @param <T>      generic type\n     * @return a new list of elements the were passed as method parameters\n     */\n    public static <T> LinkedList<T> of(T... elements) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Adds an element to the end of the list.\n     *\n     * @param element element to add\n     */\n    @Override\n    public void add(T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Adds a new element to the specific position in the list. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index   an index of new element\n     * @param element element to add\n     */\n    @Override\n    public void add(int index, T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Changes the value of an list element at specific position. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index   an position of element to change\n     * @param element a new element value\n     */\n    @Override\n    public void set(int index, T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Retrieves an elements by its position index. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index element index\n     * @return an element value\n     */\n    @Override\n    public T get(int index) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the first element of the list. Operation is performed in constant time O(1)\n     *\n     * @return the first element of the list\n     * @throws java.util.NoSuchElementException if list is empty\n     */\n    @Override\n    public T getFirst() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the last element of the list. Operation is performed in constant time O(1)\n     *\n     * @return the last element of the list\n     * @throws java.util.NoSuchElementException if list is empty\n     */\n    @Override\n    public T getLast() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Removes an elements by its position index. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index element index\n     * @return deleted element\n     */\n    @Override\n    public T remove(int index) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n\n    /**\n     * Checks if a specific exists in he list\n     *\n     * @return {@code true} if element exist, {@code false} otherwise\n     */\n    @Override\n    public boolean contains(T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Checks if a list is empty\n     *\n     * @return {@code true} if list is empty, {@code false} otherwise\n     */\n    @Override\n    public boolean isEmpty() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the number of elements in the list\n     *\n     * @return number of elements\n     */\n    @Override\n    public int size() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Removes all list elements\n     */\n    @Override\n    public void clear() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java",
    "content": "package com.bobocode.cs;\n\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.Arrays;\nimport java.util.NoSuchElementException;\nimport java.util.function.Predicate;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;\n\n/**\n * A reflection-based test class for {@link LinkedList}.\n * <p>\n * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.\n *\n * @author Serhii Hryhus\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class LinkedListTest {\n    private static final Predicate<Field> NODE_FIELD = field ->\n            field.getType().getSimpleName().equals(\"Node\");\n\n    private static final Predicate<Field> HEAD_NODE_FIELD = field ->\n            field.getType().getSimpleName().equals(\"Node\")\n            && (field.getName().contains(\"head\") || field.getName().contains(\"first\"));\n\n    private static final Predicate<Field> TAIL_NODE_FIELD = field ->\n            field.getType().getSimpleName().equals(\"Node\")\n            && (field.getName().equals(\"tail\") || field.getName().contains(\"last\"));\n\n    private static final Predicate<Field> SIZE_FIELD = field ->\n            field.getName().equals(\"size\");\n\n    private static final Predicate<Field> ELEMENT_FIELD = field ->\n            field.getGenericType().getTypeName().equals(\"T\")\n            && (field.getName().contains(\"elem\") || field.getName().contains(\"value\") || field.getName().contains(\"item\"));\n\n    private LinkedList<Integer> intList = new LinkedList<>();\n\n    @Test\n    @Order(1)\n    void properNodeName() {\n        Class<?> innerClass = getInnerClass();\n\n        String name = innerClass.getSimpleName();\n\n        assertThat(name).isEqualTo(\"Node\");\n    }\n\n    @Test\n    @Order(2)\n    void properNodeFields() {\n        Class<?> innerClass = getInnerClass();\n\n        boolean hasElementField = Arrays.stream(innerClass.getDeclaredFields())\n                .anyMatch(ELEMENT_FIELD);\n\n        boolean hasNodeField = Arrays.stream(innerClass.getDeclaredFields())\n                .anyMatch(NODE_FIELD);\n\n        assertThat(hasElementField).isTrue();\n        assertThat(hasNodeField).isTrue();\n    }\n\n    private Class<?> getInnerClass() {\n        return Arrays.stream(intList.getClass().getDeclaredClasses())\n                .filter(aClass -> Modifier.isStatic(aClass.getModifiers()))\n                .findAny()\n                .orElseThrow();\n    }\n\n    @Test\n    @Order(3)\n    void addIntoEmptyList() {\n        intList.add(41);\n\n        int element = getInternalElement(0);\n\n        assertThat(element).isEqualTo(41);\n    }\n\n    @Test\n    @Order(4)\n    void addIntoEmptyListChangesSize() {\n        intList.add(41);\n\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(1);\n    }\n\n    @Test\n    @Order(5)\n    void add() {\n        intList.add(41);\n        intList.add(30);\n        intList.add(75);\n\n        int firstElement = getInternalElement(0);\n        int secondElement = getInternalElement(1);\n        int thirdElement = getInternalElement(2);\n\n        assertThat(firstElement).isEqualTo(41);\n        assertThat(secondElement).isEqualTo(30);\n        assertThat(thirdElement).isEqualTo(75);\n    }\n\n    @Test\n    @Order(6)\n    void addChangesSize() {\n        intList.add(41);\n        intList.add(30);\n        intList.add(75);\n\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(3);\n    }\n\n    @Test\n    @Order(7)\n    void addByIndex() {\n        addInternalElements(43, 5, 6, 8);\n\n        int newElementIdx = 2;\n        intList.add(newElementIdx, 66);\n\n        int elementByNewElementIndex = getInternalElement(newElementIdx);\n        int size = getInternalSize();\n\n        assertThat(elementByNewElementIndex).isEqualTo(66);\n        assertThat(getInternalElement(0)).isEqualTo(43);\n        assertThat(getInternalElement(1)).isEqualTo(5);\n        assertThat(getInternalElement(3)).isEqualTo(6);\n        assertThat(getInternalElement(4)).isEqualTo(8);\n        assertThat(size).isEqualTo(5);\n    }\n\n    @Test\n    @Order(8)\n    void addByZeroIndexWhenListIsEmpty() {\n        intList.add(0, 45);\n\n        int element = getInternalElement(0);\n        int size = getInternalSize();\n\n        assertThat(element).isEqualTo(45);\n        assertThat(size).isEqualTo(1);\n    }\n\n    @Test\n    @Order(9)\n    void addByIndexToTheEndOfList() {\n        addInternalElements(98, 64, 23, 1, 3, 4);\n\n        int newElementIndex = getInternalSize();\n        intList.add(newElementIndex, 44);\n\n        assertThat(getInternalElement(newElementIndex)).isEqualTo(44);\n        assertThat(getInternalSize()).isEqualTo(7);\n    }\n\n    @Test\n    @Order(10)\n    void addToHeadWhenListIsNotEmpty() {\n        addInternalElements(4, 6, 8, 9, 0, 2);\n\n        intList.add(0, 53);\n\n        int firstElement = getInternalElement(0);\n        int secondElement = getInternalElement(1);\n        int size = getInternalSize();\n\n        assertThat(firstElement).isEqualTo(53);\n        assertThat(secondElement).isEqualTo(4);\n        assertThat(size).isEqualTo(7);\n    }\n\n    @Test\n    @Order(11)\n    void addThrowsExceptionWhenIndexIsNegative() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.add(-1, 66));\n    }\n\n    @Test\n    @Order(12)\n    void addThrowsExceptionWhenIndexLargerThanSize() {\n        addInternalElements(4, 6, 11, 9);\n\n        int newElementIdx = 5;\n\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.add(newElementIdx, 88));\n    }\n\n    @Test\n    @Order(13)\n    void addWhenIndexEqualToSize() {\n        addInternalElements(1, 2, 3, 4, 5); // size = 5\n\n        intList.add(5, 111);\n\n        int element = getInternalElement(5);\n        int size = getInternalSize();\n\n        assertThat(element).isEqualTo(111);\n        assertThat(size).isEqualTo(6);\n    }\n\n    @Test\n    @Order(14)\n    void of() {\n        intList = LinkedList.of(43, 233, 54);\n\n        assertThat(getInternalElement(0)).isEqualTo(43);\n        assertThat(getInternalElement(1)).isEqualTo(233);\n        assertThat(getInternalElement(2)).isEqualTo(54);\n    }\n\n    @Test\n    @Order(15)\n    void ofChangeSize() {\n        intList = LinkedList.of(43, 233, 54);\n\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(3);\n    }\n\n    @Test\n    @Order(16)\n    void setByIndex() {\n        addInternalElements(34, 78, 9, 8);\n\n        int index = 2; //element = 78\n        intList.set(index, 99);\n\n        int elementOnNewElementIndex = getInternalElement(index);\n        int nextElementToNewElementIndex = getInternalElement(3);\n        int internalSize = getInternalSize();\n\n        assertThat(elementOnNewElementIndex).isEqualTo(99);\n        assertThat(nextElementToNewElementIndex).isEqualTo(8);\n        assertThat(getInternalElement(0)).isEqualTo(34);\n        assertThat(getInternalElement(1)).isEqualTo(78);\n        assertThat(internalSize).isEqualTo(4);\n    }\n\n    @Test\n    @Order(17)\n    void setFirstElementWhenListIsEmpty() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.set(0, 34));\n    }\n\n    @Test\n    @Order(18)\n    void setByIndexEqualToSize() {\n        addInternalElements(2, 3, 4); // size = 3\n\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.set(3, 222));\n    }\n\n    @Test\n    @Order(19)\n    void get() {\n        addInternalElements(25, 87, 45);\n\n        int firstElement = intList.get(0);\n        int secondElement = intList.get(1);\n        int thirdElement = intList.get(2);\n\n        assertThat(firstElement).isEqualTo(25);\n        assertThat(secondElement).isEqualTo(87);\n        assertThat(thirdElement).isEqualTo(45);\n\n    }\n\n    @Test\n    @Order(20)\n    void getByZeroIndexWhenListHasSingleElement() {\n        addInternalElements(25);\n\n        int element = intList.get(0);\n\n        assertThat(element).isEqualTo(25);\n    }\n\n    @Test\n    @Order(21)\n    void getByZeroIndexWhenListIsEmpty() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.get(0));\n    }\n\n    @Test\n    @Order(22)\n    void getWhenIndexIsNegative() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.get(-1));\n    }\n\n    @Test\n    @Order(23)\n    void getWhenIndexIsEqualToListSize() {\n        addInternalElements(33, 46, 25, 87, 45);\n\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.get(5));\n    }\n\n    @Test\n    @Order(24)\n    void getFirst() {\n        addInternalElements(31, 32, 5);\n\n        int firstElement = intList.getFirst();\n\n        assertThat(firstElement).isEqualTo(31);\n    }\n\n    @Test\n    @Order(25)\n    void getLast() {\n        addInternalElements(41, 6, 42);\n\n        int lastElement = intList.getLast();\n\n        assertThat(lastElement).isEqualTo(42);\n    }\n\n    @Test\n    @Order(26)\n    void getFirstWhenListIsEmpty() {\n        assertThatExceptionOfType(NoSuchElementException.class)\n                .isThrownBy(() -> intList.getFirst());\n    }\n\n    @Test\n    @Order(27)\n    void getLastWhenListIsEmpty() {\n        assertThatExceptionOfType(NoSuchElementException.class)\n                .isThrownBy(() -> intList.getLast());\n    }\n\n\n    @Test\n    @Order(28)\n    void remove() {\n        addInternalElements(1, 2, 3, 4, 5);\n\n        int elementIndex = 2;\n        int deletedElement = intList.remove(elementIndex); // element = 3\n\n        int replacedElement = getInternalElement(elementIndex);\n\n        assertThat(deletedElement).isEqualTo(3);\n        assertThat(replacedElement).isEqualTo(4);\n    }\n\n    @Test\n    @Order(29)\n    void removeChangesSize() {\n        addInternalElements(1, 2, 3, 4, 5);\n\n        int elementIndex = 2;\n        int deletedElement = intList.remove(elementIndex); // element = 3\n\n        int size = getInternalSize();\n\n        assertThat(deletedElement).isEqualTo(3);\n        assertThat(size).isEqualTo(4);\n    }\n\n    @Test\n    @Order(30)\n    void removeFirst() {\n        addInternalElements(4, 6, 8, 9);\n\n        int deletedElement = intList.remove(0);\n\n        int replacedElement = getInternalElement(0);\n        int size = getInternalSize();\n\n        assertThat(deletedElement).isEqualTo(4);\n        assertThat(replacedElement).isEqualTo(6);\n        assertThat(size).isEqualTo(3);\n    }\n\n    @Test\n    @Order(31)\n    void removeLast() {\n        addInternalElements(4, 6, 8, 9);\n\n        int deletedElement = intList.remove(getInternalSize() - 1);\n\n        int newLastElement = getInternalElement(getInternalSize() - 1);\n        int tailElement = (int) getNodeValue(TAIL_NODE_FIELD);\n\n        int size = getInternalSize();\n\n        assertThat(deletedElement).isEqualTo(9);\n        assertThat(newLastElement).isEqualTo(8);\n        assertThat(tailElement).isEqualTo(8);\n        assertThat(size).isEqualTo(3);\n    }\n\n    @Test\n    @Order(32)\n    void removeWhenListIsEmpty() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.remove(234));\n    }\n\n    @Test\n    @Order(33)\n    void removeByZeroIndexWhenListIsEmpty() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> intList.remove(0));\n    }\n\n    @Test\n    @Order(34)\n    void size() {\n        setInternalSize(5);\n\n        int sizeFromMethod = intList.size();\n\n        assertThat(sizeFromMethod).isEqualTo(5);\n    }\n\n    @Test\n    @Order(35)\n    void sizeWhenListIsEmpty() {\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(0);\n    }\n\n    @Test\n    @Order(36)\n    void contains() {\n        addInternalElements(45, 6, 3, 6);\n\n        boolean containsExistingElement = intList.contains(3);\n        boolean containsNotExistingElement = intList.contains(54);\n\n        assertThat(containsExistingElement).isTrue();\n        assertThat(containsNotExistingElement).isFalse();\n    }\n\n    @Test\n    @Order(37)\n    void containsWhenListIsEmpty() {\n        boolean contains = intList.contains(34);\n\n        assertThat(contains).isFalse();\n    }\n\n    @Test\n    @Order(38)\n    void isEmpty() {\n        addInternalElements(34, 5, 6);\n\n        boolean empty = intList.isEmpty();\n\n        assertThat(empty).isFalse();\n    }\n\n    @Test\n    @Order(39)\n    void isEmptyWhenListIsEmpty() {\n        boolean empty = intList.isEmpty();\n\n        assertThat(empty).isTrue();\n    }\n\n    @Test\n    @Order(40)\n    void clearWhenListIsEmpty() {\n        intList.clear();\n\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(0);\n    }\n\n    @Test\n    @Order(41)\n    void clearChangesSize() {\n        addInternalElements(4, 5, 6);\n\n        intList.clear();\n\n        int size = getInternalSize();\n\n        assertThat(size).isEqualTo(0);\n    }\n\n    @Test\n    @Order(42)\n    void clearRemovesAllElements() {\n        addInternalElements(4, 5, 6);\n\n        intList.clear();\n\n        assertThatExceptionOfType(NullPointerException.class)\n                .isThrownBy(() -> getInternalElement(0));\n    }\n\n    @SneakyThrows\n    private int getInternalElement(int index) {\n\n        Object head = getAccessibleFieldByPredicate(intList, HEAD_NODE_FIELD).get(intList);\n\n        for (int j = 0; j < index; j++) {\n            head = getAccessibleFieldByPredicate(head, NODE_FIELD).get(head);\n        }\n        return (int) getAccessibleFieldByPredicate(head, ELEMENT_FIELD).get(head);\n    }\n\n    @SneakyThrows\n    private int getInternalSize() {\n        return (int) getAccessibleFieldByPredicate(intList, SIZE_FIELD).get(intList);\n    }\n\n    @SneakyThrows\n    private void addInternalElements(int... elements) {\n        Field nodeField = getInternalHeadField();\n        Field tailNode = getInternalTailField();\n        Class<?> nodeType = nodeField.getType();\n\n        Object previousObject = intList;\n        Object nodeObject = null;\n\n        for (int element : elements) {\n            nodeObject = createNodeObjectWithInternalElement(nodeType, element);\n            nodeField.set(previousObject, nodeObject);\n            nodeField = getAccessibleFieldByPredicate(nodeObject, NODE_FIELD);\n            previousObject = nodeObject;\n        }\n\n        tailNode.set(intList, nodeObject);\n        setInternalSize(elements.length);\n    }\n\n    private Field getInternalHeadField() {\n        return getAccessibleFieldByPredicate(intList, HEAD_NODE_FIELD);\n    }\n\n    private Field getInternalTailField() {\n        return getAccessibleFieldByPredicate(intList, TAIL_NODE_FIELD);\n    }\n\n    @SneakyThrows\n    private void setInternalSize(int size) {\n        Field sizeField = getAccessibleFieldByPredicate(intList, SIZE_FIELD);\n        sizeField.setInt(intList, size);\n    }\n\n    @SneakyThrows\n    private Object createNodeObjectWithInternalElement(Class<?> nodeClass, int element) {\n        Object nodeObject;\n        Constructor<?>[] declaredConstructors = nodeClass.getDeclaredConstructors();\n        Constructor<?> constructor;\n\n        constructor = declaredConstructors[0];\n        constructor.setAccessible(true);\n        if (constructor.getParameterTypes().length == 1) {\n            nodeObject = constructor.newInstance(element);\n        } else {\n            nodeObject = createNodeByConstructorWithoutParameters(element, constructor);\n        }\n        return nodeObject;\n    }\n\n    @SneakyThrows\n    private Object createNodeByConstructorWithoutParameters(int element, Constructor<?> constructor) {\n        Object nodeObject;\n        nodeObject = constructor.newInstance();\n        Field nodeElement = getAccessibleFieldByPredicate(nodeObject, ELEMENT_FIELD);\n        nodeElement.set(nodeObject, element);\n        return nodeObject;\n    }\n\n    private Field getAccessibleFieldByPredicate(Object object, Predicate<Field> predicate) {\n        Field field = Arrays.stream(object.getClass().getDeclaredFields())\n                .filter(predicate)\n                .findAny()\n                .orElseThrow();\n        field.setAccessible(true);\n        return field;\n    }\n\n    @SneakyThrows\n    private Object getNodeValue(Predicate<Field> predicate) {\n        Object field = getAccessibleFieldByPredicate(intList, predicate).get(intList);\n        final Field value = getAccessibleFieldByPredicate(field, ELEMENT_FIELD);\n        value.setAccessible(true);\n        return value.get(field);\n    }\n}"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-5-array-list/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Array List\n\nBuild strong skills implementing a well known Array List data structure 💪\n\n### Pre-conditions ❗\n\n* You're supposed to be familiar `List` collection and generics in Java\n\n### Objectives\n\n* use array of type `Object` to store any elements ✅\n* resize array using native method `System.arrayCopy()` ✅\n* **add an element** to the end of array ✅\n* **add an element by index** (shift whole array tail to the right) ✅\n* **set element** by index ✅\n* **remove element** by index (shift whole array tail to the left) ✅\n* maintain list **size** ✅\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/jFBKToSC3ag/0.jpg)](https://www.youtube.com/watch?v=jFBKToSC3ag)\n\n---\n\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-5-array-list/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>2-0-data-structures-and-algorithms</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>2-2-5-array-list</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>data-structures-and-algorithms-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-5-array-list/src/main/java/com/bobocode/cs/ArrayList.java",
    "content": "package com.bobocode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link ArrayList} is an implementation of {@link List} interface. This resizable data structure\n * based on an array and is simplified version of {@link java.util.ArrayList}.\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @author Serhii Hryhus\n */\npublic class ArrayList<T> implements List<T> {\n\n    /**\n     * This constructor creates an instance of {@link ArrayList} with a specific capacity of an array inside.\n     *\n     * @param initCapacity - the initial capacity of the list\n     * @throws IllegalArgumentException – if the specified initial capacity is negative or 0.\n     */\n    public ArrayList(int initCapacity) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * This constructor creates an instance of {@link ArrayList} with a default capacity of an array inside.\n     * A default size of inner array is 5;\n     */\n    public ArrayList() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Creates and returns an instance of {@link ArrayList} with provided elements\n     *\n     * @param elements to add\n     * @return new instance\n     */\n    public static <T> List<T> of(T... elements) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Adds an element to the array.\n     *\n     * @param element element to add\n     */\n    @Override\n    public void add(T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Adds an element to the specific position in the array where\n     *\n     * @param index   index of position\n     * @param element element to add\n     */\n    @Override\n    public void add(int index, T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Retrieves an element by its position index. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index index of element\n     * @return en element\n     */\n    @Override\n    public T get(int index) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the first element of the list. Operation is performed in constant time O(1)\n     *\n     * @return the first element of the list\n     * @throws java.util.NoSuchElementException if list is empty\n     */\n    @Override\n    public T getFirst() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the last element of the list. Operation is performed in constant time O(1)\n     *\n     * @return the last element of the list\n     * @throws java.util.NoSuchElementException if list is empty\n     */\n    @Override\n    public T getLast() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Changes the value of array at specific position. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index   position of value\n     * @param element a new value\n     */\n    @Override\n    public void set(int index, T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Removes an elements by its position index. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index element index\n     * @return deleted element\n     */\n    @Override\n    public T remove(int index) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Checks for existing of a specific element in the list.\n     *\n     * @param element is element\n     * @return If element exists method returns true, otherwise it returns false\n     */\n    @Override\n    public boolean contains(T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Checks if a list is empty\n     *\n     * @return {@code true} if list is empty, {@code false} otherwise\n     */\n    @Override\n    public boolean isEmpty() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * @return amount of saved elements\n     */\n    @Override\n    public int size() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Removes all list elements\n     */\n    @Override\n    public void clear() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-5-array-list/src/test/java/com/bobocode/cs/ArrayListTest.java",
    "content": "package com.bobocode.cs;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\nimport java.lang.reflect.Field;\nimport java.util.NoSuchElementException;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;\n\n/**\n * A reflection-based test class for {@link ArrayList}.\n * <p>\n * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.\n *\n * @author Serhii Hryhus\n * @author Ivan Virchenko\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\nclass ArrayListTest {\n\n    private List<Integer> arrayList = new ArrayList<>();\n\n    @Test\n    @Order(1)\n    void add() {\n        arrayList.add(10);\n        arrayList.add(15);\n        arrayList.add(20);\n\n        Object[] internalArray = getTestArray();\n\n        assertThat(internalArray[0]).isEqualTo(10);\n        assertThat(internalArray[1]).isEqualTo(15);\n        assertThat(internalArray[2]).isEqualTo(20);\n    }\n\n    @Test\n    @Order(2)\n    void sizeOfEmptyArrayWrapper() {\n        assertThat(arrayList.size()).isEqualTo(0);\n    }\n\n    @Test\n    @Order(3)\n    void size() {\n        setTestSize(3);\n        assertThat(arrayList.size()).isEqualTo(3);\n    }\n\n    @Test\n    @Order(4)\n    void getElementsByIndex() {\n        fillTestArray(10, 15, 20);\n\n        assertThat(arrayList.get(0)).isEqualTo(10);\n        assertThat(arrayList.get(1)).isEqualTo(15);\n        assertThat(arrayList.get(2)).isEqualTo(20);\n        assertThat(arrayList.size()).isEqualTo(3);\n    }\n\n    @Test\n    @Order(5)\n    void getFirstElement() {\n        fillTestArray(31, 24);\n        assertThat(arrayList.getFirst()).isEqualTo(31);\n    }\n\n    @Test\n    @Order(6)\n    void getLastElement() {\n        fillTestArray(31, 34);\n        assertThat(arrayList.getLast()).isEqualTo(34);\n    }\n\n    @Test\n    @Order(7)\n    void getFirstOfEmptyList() {\n        assertThatExceptionOfType(NoSuchElementException.class)\n                .isThrownBy(() -> arrayList.getFirst());\n    }\n\n    @Test\n    @Order(8)\n    void getLastOfEmptyList() {\n        assertThatExceptionOfType(NoSuchElementException.class)\n                .isThrownBy(() -> arrayList.getLast());\n    }\n\n    @Test\n    @Order(9)\n    void createListWithSpecificArrayCapacity() {\n        arrayList = new ArrayList<>(8);\n        assertThat(getTestArray().length).isEqualTo(8);\n    }\n\n    @Test\n    @Order(10)\n    void createListWithWrongCapacity() {\n        assertThatExceptionOfType(IllegalArgumentException.class)\n                .isThrownBy(() -> arrayList = new ArrayList<>(-2));\n    }\n\n    @Test\n    @Order(11)\n    void addElements() {\n        arrayList = ArrayList.of(15, 69, 58, 78);\n\n        assertThat(getTestArray()[0]).isEqualTo(15);\n        assertThat(getTestArray()[1]).isEqualTo(69);\n        assertThat(getTestArray()[2]).isEqualTo(58);\n        assertThat(getTestArray()[3]).isEqualTo(78);\n        assertThat(getTestSize()).isEqualTo(4);\n    }\n\n    @Test\n    @Order(12)\n    void addShouldResizeDefaultCapacityWhenArrayIsFull() {\n        arrayList = new ArrayList<>();\n        int defaultCapacity = getTestArray().length;\n\n        arrayList.add(15);\n        arrayList.add(69);\n        arrayList.add(58);\n        arrayList.add(78);\n        arrayList.add(6);\n        arrayList.add(33);\n        arrayList.add(21);\n\n        assertThat(getTestArray().length).isGreaterThan(defaultCapacity);\n        assertThat(getTestSize()).isEqualTo(7);\n    }\n\n    @Test\n    @Order(13)\n    void addShouldResizeSpecificCapacityWhenArrayIsFull() {\n        arrayList = new ArrayList<>(4);\n\n        arrayList.add(15);\n        arrayList.add(69);\n        arrayList.add(58);\n        arrayList.add(78);\n        arrayList.add(6);\n        arrayList.add(33);\n        arrayList.add(21);\n\n        assertThat(getTestArray().length).isGreaterThan(4);\n        assertThat(getTestSize()).isEqualTo(7);\n    }\n\n    @Test\n    @Order(14)\n    void addElementByIndex() {\n        fillTestArray(15, 69, 58, 78, 68);\n\n        arrayList.add(50);\n        arrayList.add(2, 10);\n\n        Object[] internalArray = getTestArray();\n\n        assertThat(internalArray[2]).isEqualTo(10);\n        assertThat(internalArray[5]).isEqualTo(68);\n        assertThat(getTestSize()).isEqualTo(7);\n    }\n\n    @Test\n    @Order(15)\n    void addElementByNegativeIndex() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.add(-1, 66));\n    }\n\n    @Test\n    @Order(16)\n    void addElementByIndexLargerThanListSize() {\n        setTestSize(4);\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.add(5, 88));\n    }\n\n    @Test\n    @Order(17)\n    void addElementByIndexEqualToSize() {\n        fillTestArray(1, 2, 3, 4, 5);\n\n        arrayList.add(5, 111);\n\n        Object[] internalArray = getTestArray();\n\n        assertThat(internalArray[5]).isEqualTo(111);\n        assertThat(getTestSize()).isEqualTo(6);\n    }\n\n    @Test\n    @Order(18)\n    void getFirstElementFromEmptyList() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.get(0));\n    }\n\n    @Test\n    @Order(19)\n    void getElementByNegativeIndex() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.get(-1));\n    }\n\n    @Test\n    @Order(20)\n    void getElementByIndexThrowsExceptionWhenIndexIsOutOfBound() {\n        fillTestArray(1, 2, 3, 4);\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.get(4));\n    }\n\n    @Test\n    @Order(21)\n    void setElementByIndex() {\n        fillTestArray(15, 69, 58, 78);\n        Object[] internalArray = getTestArray();\n\n        arrayList.set(2, 10);\n\n        assertThat(internalArray[2]).isEqualTo(10);\n        assertThat(internalArray[3]).isEqualTo(78);\n        assertThat(getTestSize()).isEqualTo(4);\n    }\n\n    @Test\n    @Order(22)\n    void setElementByIndexThrowsExceptionWhenIndexIsOutOfBound() {\n        fillTestArray(15, 69, 58, 78);\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.set(4, 10));\n    }\n\n    @Test\n    @Order(23)\n    void setFirstElementOnEmptyList() {\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.set(0, 34));\n    }\n\n    @Test\n    @Order(24)\n    void removeElementByIndex() {\n        fillTestArray(15, 69, 58, 78, 100);\n        Object[] internalArray = getTestArray();\n\n        int removedElement = arrayList.remove(2);\n\n        assertThat(internalArray[2]).isEqualTo(78);\n        assertThat(internalArray[1]).isEqualTo(69);\n        assertThat(getTestSize()).isEqualTo(4);\n        assertThat(removedElement).isEqualTo(58);\n    }\n\n    @Test\n    @Order(25)\n    void removeElementByIndexThrowsExceptionWhenIndexEqualsSize() {\n        fillTestArray(15, 69, 58, 78);\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.remove(4));\n    }\n\n    @Test\n    @Order(26)\n    void removeLastElementByIndex() {\n        fillTestArray(15, 69, 58, 78, 100);\n        Object[] internalArray = getTestArray();\n\n        int removedElement = arrayList.remove(4);\n\n        assertThat(internalArray[3]).isEqualTo(78);\n        assertThat(getTestSize()).isEqualTo(4);\n        assertThat(removedElement).isEqualTo(100);\n    }\n\n    @Test\n    @Order(27)\n    void removeElementByIndexThrowsExceptionWhenIndexIsOutOfBounds() {\n        fillTestArray(15, 69, 58, 78, 100);\n\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.remove(6));\n    }\n\n    @Test\n    @Order(28)\n    void containsOnEmptyList() {\n        assertThat(arrayList.contains(8)).isEqualTo(false);\n    }\n\n    @Test\n    @Order(29)\n    void containsElement() {\n        fillTestArray(15, 69, 58, 78, 100);\n        assertThat(arrayList.contains(58)).isEqualTo(true);\n    }\n\n    @Test\n    @Order(30)\n    void containsNotExistingWhenArrayIsNotFilled() {\n        arrayList = new ArrayList<>(100);\n        Object[] internalArray = getTestArray();\n        internalArray[0] = 5;\n        internalArray[1] = 10;\n\n        boolean result = arrayList.contains(3);\n\n        assertThat(result).isFalse();\n    }\n\n    @Test\n    @Order(31)\n    void findNotExistingElement() {\n        fillTestArray(15, 69, 58, 78, 100);\n        assertThat(arrayList.contains(200)).isEqualTo(false);\n    }\n\n    @Test\n    @Order(32)\n    void isEmptyOnEmptyList() {\n        assertThat(arrayList.isEmpty()).isEqualTo(true);\n    }\n\n    @Test\n    @Order(33)\n    void isEmpty() {\n        setTestSize(3);\n        assertThat(arrayList.isEmpty()).isEqualTo(false);\n    }\n\n    @Test\n    @Order(34)\n    void clearOnEmptyList() {\n        assertThat(arrayList.isEmpty()).isEqualTo(true);\n    }\n\n    @Test\n    @Order(35)\n    void clearChangesTheSize() {\n        setTestSize(100);\n        arrayList.clear();\n\n        assertThat(arrayList.size()).isEqualTo(0);\n    }\n\n    @Test\n    @Order(36)\n    void clearRemovesElements() {\n        fillTestArray(4, 5, 6);\n\n        arrayList.clear();\n\n        assertThatExceptionOfType(IndexOutOfBoundsException.class)\n                .isThrownBy(() -> arrayList.get(0));\n    }\n\n    @SneakyThrows\n    private void setTestSize(int size) {\n        Field sizeField = arrayList.getClass().getDeclaredField(\"size\");\n        sizeField.setAccessible(true);\n        sizeField.set(arrayList, size);\n    }\n\n    @SneakyThrows\n    private int getTestSize() {\n        Field testSize = arrayList.getClass().getDeclaredField(\"size\");\n        testSize.setAccessible(true);\n        return (int) testSize.get(arrayList);\n    }\n\n    @SneakyThrows\n    private Object[] getTestArray() {\n        Field field = arrayList.getClass().getDeclaredField(getTestArrayName());\n        field.setAccessible(true);\n        return (Object[]) field.get(arrayList);\n    }\n\n    private String getTestArrayName() {\n        Field[] fields = arrayList.getClass().getDeclaredFields();\n        String name = null;\n        for (Field field : fields) {\n            if (field.getType().isArray()) {\n                field.setAccessible(true);\n                name = field.getName();\n            }\n        }\n        return name;\n    }\n\n    @SneakyThrows\n    private void fillTestArray(Object... elements) {\n        Field arrayField = arrayList.getClass().getDeclaredField(getTestArrayName());\n        Field sizeField = arrayList.getClass().getDeclaredField(\"size\");\n        arrayField.setAccessible(true);\n        sizeField.setAccessible(true);\n        arrayField.set(arrayList, elements);\n        sizeField.set(arrayList, elements.length);\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Binary Search Tree\nLearn tree data structures and build strong skills implementing binary search tree using recursion 💪\n\n### Pre-conditions ❗️\n* You're supposed to be familiar with trees like [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree)\n* You should understand the [recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science))\n\n### Objectives\n* implement a generic class `Node<T>` with left and right child nodes ✅\n* **insert  an element** to the tree using recursion ✅\n* **search an element** in the tree ✅\n* **traverse tree elements** in a ascending order ✅\n* calculate tree **depth** ✅\n* maintain tree **size** ✅\n\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/alxzyWswCVg/0.jpg)](https://www.youtube.com/watch?v=alxzyWswCVg)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>2-0-data-structures-and-algorithms</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>2-2-6-binary-search-tree</artifactId>\n\n\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/main/java/com/bobocode/cs/BinarySearchTree.java",
    "content": "package com.bobocode.cs;\n\nimport java.util.function.Consumer;\n\npublic interface BinarySearchTree<T extends Comparable<T>> {\n    /**\n     * insert an element\n     * @return true if element did not exist in the tree and was inserted successfully\n     */\n    boolean insert(T element);\n\n    /**\n     * @return true if tree contains element\n     */\n    boolean contains(T element);\n\n    /**\n     * @return number of elements in the tree\n     */\n    int size();\n\n    /**\n     * @return max. number of transition between root node and any other node; 0 - if tree is empty or contains 1 element\n     */\n    int depth();\n\n    /**\n     * traverse the tree in element's natural order\n     * @param consumer accepts ref. to node during traversing\n     */\n    void inOrderTraversal(Consumer<T> consumer);\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/main/java/com/bobocode/cs/RecursiveBinarySearchTree.java",
    "content": "package com.bobocode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.function.Consumer;\n\n/**\n * {@link RecursiveBinarySearchTree} is an implementation of a {@link BinarySearchTree} that is based on a linked nodes\n * and recursion. A tree node is represented as a nested class {@link Node}. It holds an element (a value) and\n * two references to the left and right child nodes.\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @param <T> a type of elements that are stored in the tree\n * @author Taras Boychuk\n * @author Maksym Stasiuk\n */\npublic class RecursiveBinarySearchTree<T extends Comparable<T>> implements BinarySearchTree<T> {\n\n    public static <T extends Comparable<T>> RecursiveBinarySearchTree<T> of(T... elements) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public boolean insert(T element) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public boolean contains(T element) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public int size() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public int depth() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public void inOrderTraversal(Consumer<T> consumer) {\n        throw new ExerciseNotCompletedException();\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/test/java/com/bobocode/cs/RecursiveBinarySearchTreeTest.java",
    "content": "package com.bobocode.cs;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatNullPointerException;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\n/**\n * A reflection-based test class for {@link ArrayList}.\n * <p>\n * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.\n *\n * @author Ivan Virchenko\n * @author Maksym Stasiuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\nclass RecursiveBinarySearchTreeTest {\n    private static final Predicate<Field> SIZE_FIELD = field ->\n            field.getName().toLowerCase().contains(\"size\") || field.getName().toLowerCase().contains(\"length\");\n    \n    private static final Predicate<Field> NODE_FIELD = field ->\n            field.getType().getSimpleName().equals(\"Node\");\n    \n    private static final Predicate<Field> ELEMENT_FIELD = field ->\n            field.getName().toLowerCase().contains(\"element\")\n            || field.getName().toLowerCase().contains(\"item\")\n            || field.getName().toLowerCase().contains(\"value\");\n    \n    private static final Predicate<Field> LEFT_FIELD = field ->\n            field.getName().toLowerCase().contains(\"left\")\n            && field.getType().getSimpleName().equals(\"Node\");\n    \n    private static final Predicate<Field> RIGHT_FIELD = field ->\n            field.getName().toLowerCase().contains(\"right\")\n            && field.getType().getSimpleName().equals(\"Node\");\n\n    private static final Integer[] someElements = {10, 9, 11, 8, 12, 7};\n    \n    private BinarySearchTree<Integer> tree = new RecursiveBinarySearchTree<>();\n\n    @Test\n    @Order(1)\n    void properNodeClassNameCheck() {\n        Class<?> innerClass = getInnerClass();\n        String name = innerClass.getSimpleName();\n\n        assertThat(name).isEqualTo(\"Node\");\n    }\n\n    @Test\n    @Order(2)\n    void properTreeFieldsCheck() {\n        Class<?> treeClass = tree.getClass();\n\n        boolean hasSizeField = Arrays.stream(treeClass.getDeclaredFields())\n                .anyMatch(SIZE_FIELD);\n\n        boolean hasNodeField = Arrays.stream(treeClass.getDeclaredFields())\n                .anyMatch(NODE_FIELD);\n\n        assertThat(hasSizeField).isTrue();\n        assertThat(hasNodeField).isTrue();\n    }\n\n    @Test\n    @Order(3)\n    void properNodeFieldsCheck() {\n        Class<?> innerClass = getInnerClass();\n\n        boolean isElement = Arrays.stream(innerClass.getDeclaredFields())\n                .anyMatch(ELEMENT_FIELD);\n\n        boolean isLeft = Arrays.stream(innerClass.getDeclaredFields())\n                .anyMatch(LEFT_FIELD);\n\n        boolean isRight = Arrays.stream(innerClass.getDeclaredFields())\n                .anyMatch(RIGHT_FIELD);\n\n        assertThat(isElement).isTrue();\n        assertThat(isLeft).isTrue();\n        assertThat(isRight).isTrue();\n    }\n\n    @Test\n    @Order(4)\n    void of() {\n        tree = RecursiveBinarySearchTree.of(someElements);\n        for (var e : someElements) {\n            assertThat(contains(getRootObject(), e)).isTrue();\n        }\n        assertThat(getInnerSize()).isEqualTo(someElements.length);\n    }\n\n    @Test\n    @Order(5)\n    void insert() {\n        for (Integer e : someElements) {\n            assertThat(contains(getRootObject(), e)).isFalse(); //does not contain\n            assertThat(tree.insert(e)).isTrue(); //do insert\n            assertThat(contains(getRootObject(), e)).isTrue(); //and contains\n        }\n    }\n\n    @Test\n    @Order(6)\n    void insertToRootIfTreeIsEmpty() {\n        assertThat(getRootObject()).isNull();\n        tree.insert(10);\n        assertThat(getRootObject()).isNotNull();\n    }\n\n    @Test\n    @Order(7)\n    void insertLeftIfLessThanRoot() {\n        tree.insert(10); //root\n        tree.insert(5); //left\n\n        assertThat(getLeftNode(getRootObject())).isNotNull();\n    }\n\n    @Test\n    @Order(8)\n    void insertRightIfGreaterThanRoot() {\n        tree.insert(10); //root\n        tree.insert(15); //right\n\n        assertThat(getRightNode(getRootObject())).isNotNull();\n    }\n\n    @Test\n    @Order(9)\n    void insertDoesNotAddDuplicateElements() {\n        fillTestTree(10, 11, 12);\n\n        assertThat(tree.insert(10)).isFalse();\n        assertThat(tree.insert(11)).isFalse();\n        assertThat(tree.insert(12)).isFalse();\n    }\n\n    @Test\n    @Order(10)\n    void insertThrowsExceptionWhenArgumentIsNull() {\n        fillTestTree(someElements);\n        assertThatNullPointerException().isThrownBy(\n                () -> tree.insert(null)\n        );\n    }\n\n    @Test\n    @Order(11)\n    void containsReturnsTrueIfElementExist() {\n        fillTestTree(9, 10, 11);\n\n        assertThat(tree.contains(10)).isTrue();\n        assertThat(tree.contains(9)).isTrue();\n        assertThat(tree.contains(11)).isTrue();\n    }\n\n    @Test\n    @Order(12)\n    void containsReturnsFalseIfElementDoesntExist() {\n        fillTestTree(someElements);\n        assertThat(tree.contains(100)).isFalse();\n    }\n\n    @Test\n    @Order(13)\n    void containsThrowsExceptionIFParameterIsNull() {\n        assertThatNullPointerException().isThrownBy(() -> tree.contains(null));\n    }\n\n    @Test\n    @Order(14)\n    void sizeIsGrowingWhenInserting() {\n        tree.insert(10);\n        tree.insert(15);\n        tree.insert(20);\n\n        assertThat(getInnerSize()).isEqualTo(3);\n    }\n\n    @Test\n    @Order(15)\n    void sizeDoesNotGrowWhenInsertingNotUnique() {\n        fillTestTree(10, 11, 12);\n\n        assertThat(getInnerSize()).isEqualTo(3);\n\n        tree.insert(10);\n        tree.insert(11);\n        tree.insert(12);\n\n        assertThat(getInnerSize()).isEqualTo(3);\n    }\n\n    @Order(16)\n    @ParameterizedTest\n    @MethodSource(\"depthArguments\")\n    void depth(Integer[] elements, int depth) {\n        fillTestTree(elements);\n        assertThat(tree.depth()).isEqualTo(depth);\n    }\n\n    @Test\n    @Order(17)\n    void depthGrowWhenInsert() {\n\n        tree.insert(13);\n        tree.insert(11);\n        tree.insert(12);\n        tree.insert(15);\n        tree.insert(-15);\n\n        assertThat(tree.depth()).isGreaterThan(0);\n    }\n\n    @Test\n    @Order(18)\n    void depthIsZeroIfRootIsNull() {\n        assertThat(tree.depth()).isEqualTo(0);\n    }\n\n    @Test\n    @Order(19)\n    void inorderTraversal() {\n        fillTestTree(someElements);\n        Integer[] sortedElements = Arrays.copyOf(someElements, someElements.length);\n        Arrays.sort(sortedElements);\n\n        List<Integer> traversedElements = new ArrayList<>(getInnerSize());\n        tree.inOrderTraversal(traversedElements::add);\n\n        assertThat(traversedElements).isEqualTo(List.of(sortedElements));\n    }\n\n    public static Stream<Arguments> depthArguments() {\n        return Stream.of(\n                //empty tree\n                arguments(new Integer[]{}, 0),\n                //tree with a single element\n                arguments(new Integer[]{24}, 0),\n                /*\n                 * .......10\n                 * ....../  \\\n                 * .....5   15\n                 * ..../      \\\n                 * ...1       20\n                 */\n                arguments(new Integer[]{10, 5, 15, 1, 20}, 2),\n                /*\n                 * ..1\n                 * ...\\\n                 * ....2\n                 * .....\\\n                 * ..... 3\n                 * .......\\\n                 * ........4\n                 * .........\\\n                 * ..........5\n                 */\n                arguments(new Integer[]{1, 2, 3, 4, 5}, 4),\n                /*\n                 * .........6\n                 * ....../.....\\\n                 * .....2.......7\n                 * .../...\\......\\\n                 * ..1.....5......8\n                 * ......./........\\\n                 * ......4..........9\n                 * ...../.............\n                 * ....3...............\n                 */\n                arguments(new Integer[]{6, 2, 7, 1, 5, 8, 4, 9, 3}, 4));\n    }\n\n\n    @SneakyThrows\n    private int getInnerSize() {\n        return (int) getInnerSizeField().get(tree);\n    }\n\n    private Field getInnerSizeField() {\n        Field sizeField = Arrays.stream(tree.getClass().getDeclaredFields())\n                .filter(SIZE_FIELD)\n                .findAny()\n                .orElseThrow();\n        sizeField.setAccessible(true);\n        return sizeField;\n    }\n\n    private Class<?> getInnerClass() {\n        return Arrays.stream(tree.getClass().getDeclaredClasses())\n                .filter(Class::isMemberClass)\n                .findAny()\n                .orElseThrow();\n    }\n\n    @SneakyThrows\n    private Field getRootField() {\n        Field nodeField = Arrays.stream(tree.getClass().getDeclaredFields())\n                .filter(NODE_FIELD)\n                .findAny()\n                .orElseThrow();\n        nodeField.setAccessible(true);\n        return nodeField;\n    }\n\n    @SneakyThrows\n    private Object getRootObject() {\n        Field nodeField = Arrays.stream(tree.getClass().getDeclaredFields())\n                .filter(NODE_FIELD)\n                .findAny()\n                .orElseThrow();\n        nodeField.setAccessible(true);\n        return nodeField.get(tree);\n    }\n\n    @SneakyThrows\n    private boolean contains(Object node, int element) {\n        return findNodeByElement(node, element) != null;\n    }\n\n    private Object findNodeByElement(Object node, int element) {\n        if (node == null) {\n            return null;\n        }\n        if (element == getElement(node)) {\n            return node;\n        } else if (element < getElement(node)) {\n            return findNodeByElement(getLeftNode(node), element);\n        } else if (element > getElement(node)) {\n            return findNodeByElement(getRightNode(node), element);\n        } else {\n            return node;\n        }\n    }\n\n    @SneakyThrows\n    private Field getNodesField(Object node, Predicate<Field> option) {\n        Field field = Arrays.stream(node.getClass().getDeclaredFields())\n                .filter(option)\n                .findAny()\n                .orElseThrow();\n        field.setAccessible(true);\n        return field;\n    }\n\n    @SneakyThrows\n    private int getElement(Object node) {\n        return (int) getNodesField(node, ELEMENT_FIELD).get(node);\n    }\n\n    @SneakyThrows\n    private Object getLeftNode(Object node) {\n        return getNodesField(node, LEFT_FIELD).get(node);\n    }\n\n    @SneakyThrows\n    private Object getRightNode(Object node) {\n        return getNodesField(node, RIGHT_FIELD).get(node);\n    }\n\n    @SneakyThrows\n    private Object newNode(int element) {\n        Object nodeInstance;\n        Constructor<?>[] constructors = getInnerClass().getDeclaredConstructors();\n        Constructor<?> constructor;\n\n        constructor = constructors[0];\n        constructor.setAccessible(true);\n        if (constructor.getParameters().length == 1) {\n            nodeInstance = constructor.newInstance(element);\n        } else {\n            nodeInstance = constructor.newInstance();\n            Field nodeElement = getNodesField(nodeInstance, ELEMENT_FIELD);\n            nodeElement.set(nodeInstance, element);\n        }\n        return nodeInstance;\n    }\n\n    @SneakyThrows\n    private void insertElement(int element) {\n\n        Object root = getRootObject();\n\n        if (root == null) {\n            getRootField().set(tree, newNode(element));\n        } else {\n            insertToSubtree(root, element);\n        }\n    }\n\n    private boolean insertToSubtree(Object node, int element) {\n        if (element < getElement(node)) {\n            return insertLeft(node, element);\n        } else if (element > getElement(node)) {\n            return insertRight(node, element);\n        } else {\n            return false;\n        }\n    }\n\n    @SneakyThrows\n    private boolean insertLeft(Object node, int element) {\n        if (getLeftNode(node) != null) {\n            return insertToSubtree(getLeftNode(node), element);\n        } else {\n            getNodesField(node, LEFT_FIELD).set(node, newNode(element));\n            return true;\n        }\n    }\n\n    @SneakyThrows\n    private boolean insertRight(Object node, int element) {\n        if (getRightNode(node) != null) {\n            return insertToSubtree(getRightNode(node), element);\n        } else {\n            getNodesField(node, RIGHT_FIELD).set(node, newNode(element));\n            return true;\n        }\n    }\n\n    @SneakyThrows\n    private void fillTestTree(Integer... elements) {\n        tree = new RecursiveBinarySearchTree<>();\n        for (Integer e : elements) {\n            insertElement(e);\n        }\n        getInnerSizeField().set(tree, elements.length);\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Hash Table\nLearn a Hash Table data structures and build strong skills creating a hash table-based implementation of a Map interface 💪\n\n### Objectives (at the end of the training you will be able to...)\n* understand the **main idea** behind the **Hash Table** data structure ✅\n* explain why do we need a `hashCode` method and how it speeds up the search ✅\n* implement key-value class `Node<K,V>` ✅\n* implement a simple `HashTable` based on the array of linked `Node` objects\n* realize the limitations of the Hash Table ✅\n* understand why do we need the resizing logic and how does it work ✅\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>2-0-data-structures-and-algorithms</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>2-2-9-hash-table</artifactId>\n\n\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/HashTable.java",
    "content": "package com.bobocode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link HashTable} is a simple Hashtable-based implementation of {@link Map} interface with some additional methods.\n * It is based on the array of {@link Node} objects. Both {@link HashTable} and {@link Node} have two type parameters:\n * K and V, which represent key and value.\n * <p>\n * Elements are stored int the table by their key. A table is basically an array, and fast access is possible due to\n * array capabilities. (You can access an array element by its index in O(1) time). In order to find an index for any\n * given key, it uses calculateIndex method which is based on the element's hash code.\n * <p>\n * If two elements (keys) have the same array index, they form a linked list. That's why class {@link Node} requires\n * a reference to the next field.\n * <p>\n * Since you don't always know the number of elements in advance, the table can be resized. You can do that manually by\n * calling method resizeTable, or it will be done automatically once the table reach resize threshold.\n * <p>\n * The initial array size (initial capacity) is 8.\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @param <K> key type\n * @param <V> value type\n * @author Taras Boychuk\n */\npublic class HashTable<K, V> implements Map<K, V> {\n\n    /**\n     * This method is a critical part of the hast table. The main idea is that having a key, you can calculate its index\n     * in the array using the hash code. Since the computation is done in constant time (O(1)), it's faster than\n     * any other kind search.\n     * <p>\n     * It's a function that accepts a key and calculates its index using a hash code. Please note that index cannot be\n     * equal or greater than array size (table capacity).\n     * <p>\n     * This method is used for all other operations (put, get, remove).\n     *\n     * @param key\n     * @param tableCapacity underlying array size\n     * @return array index of the given key\n     */\n    public static int calculateIndex(Object key, int tableCapacity) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Creates a mapping between provided key and value, and returns the old value. If there was no such key, it returns\n     * null. {@link HashTable} does not support duplicate keys, so if you put the same key it just overrides the value.\n     * <p>\n     * It uses calculateIndex method to find the corresponding array index. Please note, that even different keys can\n     * produce the same array index.\n     *\n     * @param key\n     * @param value\n     * @return old value or null\n     */\n    @Override\n    public V put(K key, V value) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Retrieves a value by the given key. It uses calculateIndex method to find the corresponding array index.\n     * Then it iterates though all elements that are stored by that index, and uses equals to compare its keys.\n     *\n     * @param key\n     * @return value stored in the table by the given key or null if there is no such key\n     */\n    @Override\n    public V get(K key) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Checks if the table contains a given key.\n     *\n     * @param key\n     * @return true is there is such key in the table or false otherwise\n     */\n    @Override\n    public boolean containsKey(K key) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Checks if the table contains a given value.\n     *\n     * @param value\n     * @return true is there is such value in the table or false otherwise\n     */\n    @Override\n    public boolean containsValue(V value) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Return a number of elements in the table.\n     *\n     * @return size\n     */\n    @Override\n    public int size() {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Checks is the table is empty.\n     *\n     * @return true is table size is zero or false otherwise\n     */\n    @Override\n    public boolean isEmpty() {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Removes an element by its key and returns a removed value. If there is no such key in the table, it returns null.\n     *\n     * @param key\n     * @return removed value or null\n     */\n    @Override\n    public V remove(K key) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * It's a special toString method dedicated to help you visualize a hash table. It creates a string that represents\n     * an underlying array as a table. It has multiples rows. Every row starts with an array index followed by \": \".\n     * Then it adds every key and value (key=value) that have a corresponding index. Every \"next\" reference is\n     * represented as an arrow like this \" -> \".\n     * <p>\n     * E.g. imagine a table, where the key is a string username, and the value is the number of points of that user.\n     * Is this case method toString can return something like this:\n     * <pre>\n     * 0: johnny=439\n     * 1:\n     * 2: madmax=833 -> leon=886\n     * 3:\n     * 4: altea=553\n     * 5:\n     * 6:\n     * 7:\n     * </pre>\n     *\n     * @return\n     */\n    @Override\n    public String toString() {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n\n    /**\n     * Creates a new underlying table with a given size and adds all elements to the new table.\n     * <p>\n     * In order to allow a fast access, this hash table needs to have a sufficient capacity.\n     * (You can imagine a hash table, with a default capacity of 8 that stores hundreds of thousands of elements.\n     * In that case it's just 8 huge linked lists. That's why we need this method.)\n     * <p>\n     * PLEASE NOTE that such method <strong>should not be a part of the public API</strong>, but it was made public\n     * for learning purposes. You can create a table, print it using toString, then resizeTable and print it again.\n     * It will help you to understand how it works.\n     *\n     * @param newCapacity a size of the new underlying array\n     */\n    public void resizeTable(int newCapacity) {\n        throw new ExerciseNotCompletedException(); // todo:\n    }\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/Map.java",
    "content": "package com.bobocode.cs;\n\n/**\n * A {@link Map} is a simplified interface of so-called dictionary. It maps keys to values and provides an API for data\n * access and manipulation. Please note that a map does not support duplicate keys.\n * <p>\n * It was added as a simple contact on top of a {@link HashTable} class.\n *\n * @param <K> key type\n * @param <V> value type\n * @author Taras Boychuk\n */\npublic interface Map<K, V> {\n    /**\n     * Creates or updates a mapping for a given key and value. If the key is new, it creates a mapping and return null.\n     * If the key exists, it updates the value and returns the old value.\n     *\n     * @param key\n     * @param value\n     * @return an old value or null\n     */\n    V put(K key, V value);\n\n    /**\n     * Returns the value that is mapped to the given key, or null if there is no such key.\n     *\n     * @param key\n     * @return the value that is mapped to the given key, or null if there is no such key\n     */\n    V get(K key);\n\n    /**\n     * Returns true if a given key exist or false otherwise.\n     *\n     * @param key\n     * @return true if a given key exist or false otherwise\n     */\n    boolean containsKey(K key);\n\n    /**\n     * Returns true if a given value exist or false otherwise.\n     *\n     * @param value\n     * @return true if a given value exist or false otherwise\n     */\n    boolean containsValue(V value);\n\n    /**\n     * Returns the number of entries (key-value mappings).\n     *\n     * @return the number of entries\n     */\n    int size();\n\n    /**\n     * Returns true if there is no entries or false otherwise.\n     *\n     * @return true if there is no entries or false otherwise\n     */\n    boolean isEmpty();\n\n    /**\n     * Removes a mapping for a given key, and returns a removed value. If there is no such key, it just returns null.\n     *\n     * @param key\n     * @return a removed value or null\n     */\n    V remove(K key);\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java",
    "content": "package com.bobocode.cs;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.ClassOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.*;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static java.lang.reflect.Modifier.isStatic;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * A Reflection-based step by step test for a {@link HashTable} class. PLEASE NOTE that Reflection API should not be used\n * for testing a production code. We use it for learning purposes only!\n *\n * @author Taras Boychuk\n */\n@TestClassOrder(OrderAnnotation.class)\n@DisplayName(\"HashTable Test\")\nclass HashTableTest {\n\n    private HashTable<String, Integer> hashTable = new HashTable<>();\n\n    @Nested\n    @Order(1)\n    @DisplayName(\"1. Node Test\")\n    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n    class NodeClassTest {\n\n        @Test\n        @Order(1)\n        @DisplayName(\"A nested class Node exists\")\n        void nodeClassExists() {\n            var nestedClassList = Arrays.stream(HashTable.class.getDeclaredClasses())\n                    .map(Class::getSimpleName)\n                    .toList();\n\n            assertThat(nestedClassList).contains(\"Node\");\n        }\n\n        @Test\n        @Order(2)\n        @DisplayName(\"Node is a static nested class\")\n        @SneakyThrows\n        void nodeIsStaticClass() {\n            var nodeClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n\n            assertTrue(isStatic(nodeClass.getModifiers()));\n        }\n\n        @Test\n        @Order(3)\n        @DisplayName(\"Node class has two type parameters\")\n        @SneakyThrows\n        void nodeHasTwoTypeParams() {\n            var nodeClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n            var typeParametersList = nodeClass.getTypeParameters();\n\n            assertThat(typeParametersList).hasSize(2);\n        }\n\n        @Test\n        @Order(4)\n        @DisplayName(\"Node class has 'key' and 'value' fields\")\n        @SneakyThrows\n        void nodeHasKeyValuesFields() {\n            var nodeClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n            var fieldNames = Arrays.stream(nodeClass.getDeclaredFields())\n                    .map(Field::getName)\n                    .toList();\n\n            assertThat(fieldNames).contains(\"key\").contains(\"value\");\n        }\n\n        @Test\n        @Order(5)\n        @DisplayName(\"Node class has a field 'next'\")\n        @SneakyThrows\n        void nodeHasReferenceToNext() {\n            var nodeClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n            var fieldNames = Arrays.stream(nodeClass.getDeclaredFields())\n                    .map(Field::getName)\n                    .toList();\n\n            assertThat(fieldNames).contains(\"next\");\n        }\n\n        @Test\n        @Order(6)\n        @DisplayName(\"Node class has one constructor\")\n        @SneakyThrows\n        void nodeHasOneConstructor() {\n            var nodeClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n\n            var constructors = nodeClass.getDeclaredConstructors();\n\n            assertThat(constructors).hasSize(1);\n        }\n\n        @Test\n        @Order(7)\n        @DisplayName(\"Node constructor accept key and value\")\n        @SneakyThrows\n        void nodeConstructorAcceptKeyValue() {\n            var nodeClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n\n            var constructor = nodeClass.getDeclaredConstructors()[0];\n\n            assertThat(constructor.getParameters()).hasSize(2);\n            assertThat(constructor.getParameters()[0].getName()).isEqualTo(\"key\");\n            assertThat(constructor.getParameters()[1].getName()).isEqualTo(\"value\");\n        }\n    }\n\n    @Nested\n    @Order(2)\n    @DisplayName(\"2. HashTable fields Test\")\n    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n    class HashTableFieldsTest {\n\n        @Test\n        @Order(1)\n        @DisplayName(\"HastTable has a field 'table' which is an array of nodes\")\n        @SneakyThrows\n        void tableFieldExists() {\n            var tableField = HashTable.class.getDeclaredField(\"table\");\n            var tableType = tableField.getType();\n            var nodeClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n\n            assertTrue(tableType.isArray());\n            assertThat(tableType.getComponentType()).isEqualTo(nodeClass);\n        }\n\n        @Test\n        @Order(2)\n        @DisplayName(\"HashTable has an integer field 'size'\")\n        @SneakyThrows\n        void sizeFieldExists() {\n            var sizeField = HashTable.class.getDeclaredField(\"size\");\n\n            assertThat(sizeField.getType()).isEqualTo(int.class);\n        }\n    }\n\n    @Nested\n    @Order(3)\n    @DisplayName(\"3. HashTable constructors Test\")\n    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n    class HashTableConstructorsTest {\n\n        @Test\n        @Order(1)\n        @SneakyThrows\n        @DisplayName(\"A default constructor initializes an array with default size 8\")\n        void defaultConstructor() {\n            var defaultConstructor = HashTable.class.getConstructor();\n\n            var hashTable = defaultConstructor.newInstance();\n            var table = getInternalTable(hashTable);\n\n            assertThat(table).hasSize(8);\n        }\n\n        @Test\n        @Order(2)\n        @SneakyThrows\n        @DisplayName(\"An additional constructor accepts an initial array size\")\n        void constructorWithTableCapacity() {\n            var constructor = HashTable.class.getConstructor(int.class);\n\n            var hashTable = constructor.newInstance(16);\n            var table = getInternalTable(hashTable);\n\n            assertThat(table).hasSize(16);\n        }\n\n        @Test\n        @Order(3)\n        @SneakyThrows\n        @DisplayName(\"An additional constructor throws exception when argument is negative\")\n        void constructorWithTableCapacityWhenArgumentIsNegative() {\n            var constructor = HashTable.class.getConstructor(int.class);\n\n            assertThatThrownBy(() -> constructor.newInstance(-2))\n                    .hasCauseInstanceOf(IllegalArgumentException.class);\n        }\n    }\n\n    @Nested\n    @Order(4)\n    @DisplayName(\"4. Hash Function Test\")\n    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n    class HashFunctionTest {\n\n        @Test\n        @Order(1)\n        @DisplayName(\"calculateIndex returns the same value for the same key\")\n        void calculateIndexReturnTheSameValueWhenKeyIsTheSame() {\n            var indexSet = Stream.generate(() -> \"ASDFDFSD34234234\")\n                    .limit(10)\n                    .map(key -> HashTable.calculateIndex(key, 8))\n                    .collect(Collectors.toSet());\n\n            assertThat(indexSet).hasSize(1);\n        }\n\n        @Test\n        @Order(2)\n        @DisplayName(\"calculateIndex returns different values for different keys\")\n        void calculateIndexReturnDifferentValuesWheKeysAreDifferent() {\n            var arrayCapacity = 8;\n            var indexSet = Stream.of(\"A\", \"Aa\", \"AaB\", \"4234\", \"2234fasdf\", \"ASDFDFSD34234234\", \"afsd-fdfd-ae43-5gd3\")\n                    .map(str -> HashTable.calculateIndex(str, arrayCapacity))\n                    .collect(Collectors.toSet());\n\n            assertThat(indexSet)\n                    .hasSizeGreaterThan(1);\n        }\n\n        @Test\n        @Order(3)\n        @DisplayName(\"calculateIndex returns values in array bounds\")\n        void calculateIndexReturnIndexInArrayBounds() {\n            var arrayCapacity = 8;\n            var keys = Stream.generate(() -> ThreadLocalRandom.current().nextLong())\n                    .limit(100)\n                    .toList();\n\n            var indexes = keys.stream()\n                    .map(key -> HashTable.calculateIndex(key, arrayCapacity))\n                    .toList();\n\n            assertThat(indexes)\n                    .isNotEmpty()\n                    .allMatch(i -> i >= 0 && i < arrayCapacity);\n        }\n\n        @Test\n        @Order(4)\n        @DisplayName(\"calculateIndex return non-negative value when hashCode is negative\")\n        void calculateIndexReturnPositiveIndexWhenHashCodeIsNegative() {\n            var key = Long.MAX_VALUE;\n\n            var index = HashTable.calculateIndex(key, 8);\n\n            assertThat(index).isNotNegative();\n        }\n    }\n\n    @Nested\n    @Order(5)\n    @DisplayName(\"5. HashTable methods Test\")\n    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n    class HashTableMethodsTest {\n\n        @Test\n        @SneakyThrows\n        @Order(1)\n        @DisplayName(\"put creates new entry and returns null when the table is empty, should increase the table size\")\n        void putWhenTableIsEmpty() {\n            var previousValue = hashTable.put(\"madmax\", 833);\n\n            var keyValueExists = checkKeyValueExists(\"madmax\", 833);\n\n            assertNull(previousValue);\n            assertTrue(keyValueExists);\n            assertEquals(1, getSize());\n        }\n\n        @Test\n        @Order(2)\n        @DisplayName(\"put elements adds entry to the same bucket and increases table size when the hash code is the same\")\n        @SneakyThrows\n        void putTwoElementsWithTheSameHashCode() {\n            var table = getInternalTable(hashTable);\n            var prevValueA = hashTable.put(\"AaAa\", 123);\n            var prevValueB = hashTable.put(\"BBBB\", 456);\n            var containsKeyValueA = checkKeyValueExists(\"AaAa\", 123);\n            var containsKeyValueB = checkKeyValueExists(\"BBBB\", 456);\n            var bucketIndexA = HashTable.calculateIndex(\"AaAa\", table.length);\n            var bucketIndexB = HashTable.calculateIndex(\"BBBB\", table.length);\n\n            assertNull(prevValueA);\n            assertNull(prevValueB);\n            assertTrue(containsKeyValueA);\n            assertTrue(containsKeyValueB);\n            assertThat(bucketIndexA).isEqualTo(bucketIndexB);\n            assertEquals(2, getSize());\n        }\n\n        @Test\n        @Order(3)\n        @DisplayName(\n                \"put element updates the value and returns the previous one when key is the same, should not increase table size\")\n        void putElementWithTheSameKey() {\n            hashTable.put(\"madmax\", 833);\n\n            var previousValue = hashTable.put(\"madmax\", 876);\n            var containsNewValueByKey = checkKeyValueExists(\"madmax\", 876);\n\n            assertThat(previousValue).isEqualTo(833);\n            assertTrue(containsNewValueByKey);\n            assertEquals(1, getSize());\n        }\n\n        @Test\n        @Order(4)\n        @DisplayName(\"get returns null when given key does not exists\")\n        void getElementWhenKeyDoesNotExists() {\n            var foundValue = hashTable.get(\"xxx\");\n\n            assertNull(foundValue);\n        }\n\n        @Test\n        @Order(5)\n        @DisplayName(\"get returns a corresponding value by the given key\")\n        void getWhenKeyExists() {\n            addToTable(\"madmax\", 833);\n\n            var foundValue = hashTable.get(\"madmax\");\n\n            assertThat(foundValue).isEqualTo(833);\n        }\n\n        @Test\n        @Order(6)\n        @DisplayName(\"get returns a corresponding value when there are other keys with the same index\")\n        void getWhenOtherKeyHaveTheSameIndex() {\n            addToTable(\"madmax\", 833);\n            addToTable(\"AaAa\", 654);\n            addToTable(\"BBBB\", 721);\n\n            var foundValue = hashTable.get(\"BBBB\");\n\n            assertThat(foundValue).isEqualTo(721);\n        }\n\n        @Test\n        @Order(7)\n        @DisplayName(\"containsKey returns true if element exists\")\n        void containsKeyWhenElementExists() {\n            addToTable(\"madmax\", 833);\n\n            var result = hashTable.containsKey(\"madmax\");\n\n            assertThat(result).isTrue();\n        }\n\n        @Test\n        @Order(8)\n        @DisplayName(\"containsKey returns false if element does not exist\")\n        void containsKeyWhenElementDoesNotExist() {\n            var result = hashTable.containsKey(\"madmax\");\n\n            assertThat(result).isFalse();\n        }\n\n        @Test\n        @Order(9)\n        @DisplayName(\"containsValue returns true if value exists\")\n        void containsValue() {\n            addToTable(\"madmax\", 833);\n\n            var result = hashTable.containsValue(833);\n\n            assertThat(result).isTrue();\n        }\n\n        @Test\n        @Order(9)\n        @DisplayName(\"containsValue returns false if value does not exist\")\n        void containsValueWhenItDoesNotExist() {\n            addToTable(\"madmax\", 833);\n\n            var result = hashTable.containsValue(666);\n\n            assertThat(result).isFalse();\n        }\n\n        @Test\n        @Order(10)\n        @DisplayName(\"containsValue returns true if the same value appears multiple times\")\n        void containsValueWhenValueAppearsMultipleTimes() {\n            addToTable(\"madmax\", 833);\n            addToTable(\"bobby\", 833);\n            addToTable(\"altea\", 833);\n\n            var result = hashTable.containsValue(833);\n\n            assertThat(result).isTrue();\n        }\n\n        @Test\n        @Order(11)\n        @SneakyThrows\n        @DisplayName(\"size returns the number of entries in the table\")\n        void size() {\n            setSize(12);\n\n            var size = hashTable.size();\n\n            assertThat(size).isEqualTo(12);\n        }\n\n        @Test\n        @Order(12)\n        @DisplayName(\"isEmpty returns false when there are some elements\")\n        void isEmptyWhenTableGetsElements() {\n            addToTable(\"madmax\", 833);\n            setSize(1);\n\n            var empty = hashTable.isEmpty();\n\n            assertFalse(empty);\n        }\n\n        @Test\n        @Order(13)\n        @DisplayName(\"isEmpty returns true when there is no elements\")\n        void isEmptyWhenThereIsNoElements() {\n            var empty = hashTable.isEmpty();\n\n            assertTrue(empty);\n        }\n\n        @Test\n        @Order(13)\n        @DisplayName(\"remove deletes the entry, decreases table size and returns a value\")\n        void remove() {\n            addToTable(\"madmax\", 833);\n            setSize(1);\n            var result = hashTable.remove(\"madmax\");\n\n            assertThat(result).isEqualTo(833);\n            assertFalse(checkKeyValueExists(\"madmaxx\", 833));\n            assertEquals(0, getSize());\n        }\n\n        @Test\n        @Order(14)\n        @DisplayName(\"remove returns null when key does not exists\")\n        void removeWhenKeyDoesNotExists() {\n            var result = hashTable.remove(\"madmax\");\n\n            assertNull(result);\n        }\n\n        @Test\n        @Order(15)\n        @DisplayName(\"remove deletes the element when it's in the middle of the list and decreases the size of table\")\n        void removeFromTheMiddleOfTheList() {\n            addToTable(\"AaAa\", 843);\n            addToTable(\"BBBB\", 434);\n            addToTable(\"AaBB\", 587);\n\n            var size = 3;\n            setSize(size);\n            var removedValue = hashTable.remove(\"BBBB\");\n\n            assertTrue(checkKeyValueExists(\"AaAa\", 843));\n            assertFalse(checkKeyExists(\"BBBB\"));\n            assertTrue(checkKeyValueExists(\"AaBB\", 587));\n            assertThat(removedValue).isEqualTo(434);\n            assertEquals(size - 1, getSize());\n        }\n\n        @Test\n        @Order(16)\n        @DisplayName(\"remove deletes the element when it's in the end of the list and decreases the size of table\")\n        void removeFromTheEndOfTheList() {\n            addToTable(\"AaAa\", 843);\n            addToTable(\"BBBB\", 434);\n            addToTable(\"AaBB\", 587);\n            var size = 3;\n            setSize(size);\n\n            var removedValue = hashTable.remove(\"AaBB\");\n\n            assertTrue(checkKeyValueExists(\"AaAa\", 843));\n            assertTrue(checkKeyValueExists(\"BBBB\", 434));\n            assertFalse(checkKeyExists(\"AaBB\"));\n            assertThat(removedValue).isEqualTo(587);\n            assertEquals(2, getSize());\n        }\n    }\n\n    @Nested\n    @Order(6)\n    @DisplayName(\"6. Helper methods Test\")\n    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n    class HashTableHelperMethodsTest {\n\n        @Test\n        @Order(1)\n        @DisplayName(\"resizeTable creates a new array and put there all elements\")\n        void resizeTable() {\n            addToTable(\"madmax\", 833);\n            addToTable(\"altea\", 553);\n            addToTable(\"AaAa\", 123);\n            addToTable(\"BBBB\", 456);\n\n            hashTable.resizeTable(16);\n\n            assertThat(getInternalTable(hashTable)).hasSize(16);\n            assertTrue(checkKeyValueExists(\"madmax\", 833));\n            assertTrue(checkKeyValueExists(\"altea\", 553));\n            assertTrue(checkKeyValueExists(\"AaAa\", 123));\n            assertTrue(checkKeyValueExists(\"BBBB\", 456));\n        }\n\n        @Test\n        @DisplayName(\"toString returns a string that represents an underlying table\")\n        void toStringTest() {\n            addToTable(\"madmax\", 833);\n            addToTable(\"altea\", 553);\n            addToTable(\"johnny\", 439);\n            addToTable(\"leon\", 886);\n            var table = getInternalTable(hashTable);\n            var expectedString = tableToString(table);\n\n            String tableStr = hashTable.toString();\n\n            assertThat(tableStr).isEqualTo(expectedString);\n        }\n\n    }\n\n    // Util methods\n    @SneakyThrows\n    private Object[] getInternalTable(HashTable<?, ?> hashTable) {\n        var tableField = HashTable.class.getDeclaredField(\"table\");\n        tableField.setAccessible(true);\n        return (Object[]) tableField.get(hashTable);\n    }\n\n    private void addToTable(String key, Integer value) {\n        var table = getInternalTable(hashTable);\n        var index = HashTable.calculateIndex(key, table.length);\n        var newNode = createNewNode(key, value);\n        if (table[index] == null) {\n            table[index] = newNode;\n        } else {\n            var current = new NodeProxy(table[index]);\n            while (current.next() != null && !current.key().equals(key)) {\n                current = current.next();\n            }\n            if (current.key().equals(key)) {\n                current.setValue(value);\n            } else {\n                current.setNext(newNode);\n            }\n        }\n    }\n\n    private NodeProxy getNodeByKey(Object key) {\n        var table = getInternalTable(hashTable);\n        for (var head : table) {\n            if (head != null) {\n                var current = new NodeProxy(head);\n                while (current != null) {\n                    if (current.key().equals(key)) {\n                        return current;\n                    }\n                    current = current.next();\n                }\n            }\n        }\n        return null;\n    }\n\n    private boolean checkKeyValueExists(Object key, Object value) {\n        var node = getNodeByKey(key);\n        return node != null && node.value().equals(value);\n    }\n\n    private boolean checkKeyExists(Object key) {\n        var node = getNodeByKey(key);\n        return node != null;\n    }\n\n    @SneakyThrows\n    private void setSize(int size) {\n        var sizeField = HashTable.class.getDeclaredField(\"size\");\n        sizeField.setAccessible(true);\n        sizeField.set(hashTable, size);\n    }\n\n    @SneakyThrows\n    private int getSize() {\n        var sizeField = HashTable.class.getDeclaredField(\"size\");\n        sizeField.setAccessible(true);\n        return sizeField.getInt(hashTable);\n    }\n\n    private String tableToString(Object[] table) {\n        StringBuilder result = new StringBuilder();\n        var n = table.length;\n        for (int i = 0; i < n; i++) {\n            result.append(i).append(\": \");\n            if (table[i] != null) {\n                var current = new NodeProxy(table[i]);\n                while (current.next() != null) {\n                    result.append(current.key()).append(\"=\").append(current.value()).append(\" -> \");\n                    current = current.next();\n                }\n                result.append(current.key()).append(\"=\").append(current.value());\n            }\n            result.append(\"\\n\");\n        }\n        return result.toString();\n    }\n\n    @SneakyThrows\n    private Object createNewNode(String key, Integer value) {\n        var nodeClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n        var constructor = nodeClass.getConstructor(Object.class, Object.class);\n        return constructor.newInstance(key, value);\n    }\n\n    static class NodeProxy {\n\n        Class<?> targetClass;\n        Field keyField;\n        Field valueField;\n        Field nextField;\n        Object target;\n\n        @SneakyThrows\n        public NodeProxy(Object target) {\n            Objects.requireNonNull(target);\n            this.targetClass = Class.forName(\"com.bobocode.cs.HashTable$Node\");\n            this.target = target;\n            this.keyField = targetClass.getDeclaredField(\"key\");\n            this.keyField.setAccessible(true);\n            this.valueField = targetClass.getDeclaredField(\"value\");\n            this.valueField.setAccessible(true);\n            this.nextField = targetClass.getDeclaredField(\"next\");\n            this.nextField.setAccessible(true);\n        }\n\n        @SneakyThrows\n        public Object key() {\n            return keyField.get(target);\n        }\n\n        @SneakyThrows\n        public Object value() {\n            return valueField.get(target);\n        }\n\n        @SneakyThrows\n        public NodeProxy next() {\n            return Optional.ofNullable(nextField.get(target))\n                    .map(NodeProxy::new)\n                    .orElse(null);\n        }\n\n        @SneakyThrows\n        public void setValue(Object value) {\n            valueField.set(target, value);\n        }\n\n        @SneakyThrows\n        public void setNext(Object newNode) {\n            nextField.set(target, newNode);\n        }\n    }\n}"
  },
  {
    "path": "2-0-data-structures-and-algorithms/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Data Structures & Angorithms\nLearn Data Structures & Algorithms and build strong related skills needed for enterprise Java development 💪 "
  },
  {
    "path": "2-0-data-structures-and-algorithms/data-structures-and-algorithms-util/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>2-0-data-structures-and-algorithms</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>data-structures-and-algorithms-util</artifactId>\n\n\n</project>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/data-structures-and-algorithms-util/src/main/java/com/bobocode/cs/List.java",
    "content": "package com.bobocode.cs;\n\n\npublic interface List<T> {\n    void add(T element);\n\n    void add(int index, T element);\n\n    void set(int index, T element);\n\n    T get(int index);\n\n    T getFirst();\n\n    T getLast();\n\n    T remove(int index);\n\n    boolean contains(T element);\n\n    boolean isEmpty();\n\n    int size();\n\n    void clear();\n}\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>pom</packaging>\n    <modules>\n        <module>2-2-1-node</module>\n        <module>2-2-2-stack</module>\n        <module>2-2-3-linked-queue</module>\n        <module>2-2-4-linked-list</module>\n        <module>2-2-5-array-list</module>\n        <module>2-2-6-binary-search-tree</module>\n        <module>2-2-9-hash-table</module>\n        <module>data-structures-and-algorithms-util</module>\n    </modules>\n\n    <parent>\n        <groupId>com.bobocode</groupId>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <artifactId>2-0-data-structures-and-algorithms</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>java-fundamentals-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n            <scope>compile</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>File Reader\nImprove your Java SE skills by implementing a logic that reads file content using Stream API 💪\n \n### Pre-conditions ❗\nYou're supposed to know the basics of Stream API and how to work with files in Java \n\n### Objectives\n* **find a text file** in the classpath ✅\n* **open a** `Stream<String>` of file lines ✅\n* **collect** file content into a single `String` ✅\n* **deal with exceptions** when accessing the file ✅\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>3-0-java-core</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>3-6-1-file-reader</artifactId>\n\n\n</project>"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/main/java/com/bobocode/se/FileReaders.java",
    "content": "package com.bobocode.se;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link FileReaders} provides an API that allow to read whole file into a {@link String} by file name.\n */\npublic class FileReaders {\n\n    /**\n     * Returns a {@link String} that contains whole text from the file specified by name.\n     *\n     * @param fileName a name of a text file\n     * @return string that holds whole file content\n     */\n    public static String readWholeFile(String fileName) {\n        throw new ExerciseNotCompletedException(); //todo\n    }\n}\n"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/test/java/com/bobocode/se/FileReadersTest.java",
    "content": "package com.bobocode.se;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\npublic class FileReadersTest {\n\n    @Test\n    void testReadWholeFileOnEmptyFile() {\n        String fileContent = FileReaders.readWholeFile(\"empty.txt\");\n\n        assertEquals(\"\", fileContent);\n\n    }\n\n    @Test\n    void testReadWholeFileOnFileWithEmptyLines() {\n        String fileContent = FileReaders.readWholeFile(\"lines.txt\");\n\n        assertEquals(\"Hey!\\n\" +\n                \"\\n\" +\n                \"What's up?\\n\" +\n                \"\\n\" +\n                \"Hi!\", fileContent);\n    }\n\n    @Test\n    void testReadWholeFile() {\n        String fileContent = FileReaders.readWholeFile(\"simple.txt\");\n\n        assertEquals(\"Hello!\\n\" + \"It's a test file.\", fileContent);\n    }\n}\n"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/test/resources/empty.txt",
    "content": ""
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/test/resources/lines.txt",
    "content": "Hey!\n\nWhat's up?\n\nHi!"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/test/resources/simple.txt",
    "content": "Hello!\nIt's a test file."
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Files Stats\nImprove your Stream API skills calculating character statistic using a text file 💪\n\n### Pre-conditions ❗\nYou're supposed to know how to work with text files and be able to write Java code\n\n### Objectives\n\n* **find a text file** in the classpath ✅\n* **open a** `Stream<String>` of file lines ✅\n* **transform** a stream of lines into a stream of characters\n* **group** characters by value and calculate needed stats ✅\n* **deal with exceptions** when accessing the file ✅\n\n---\n\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>3-0-java-core</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>3-6-2-file-stats</artifactId>\n\n\n</project>"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/main/java/com/bobocode/se/FileStats.java",
    "content": "package com.bobocode.se;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link FileStats} provides an API that allow to get character statistic based on text file. All whitespace characters\n * are ignored.\n */\npublic class FileStats {\n    /**\n     * Creates a new immutable {@link FileStats} objects using data from text file received as a parameter.\n     *\n     * @param fileName input text file name\n     * @return new FileStats object created from text file\n     */\n    public static FileStats from(String fileName) {\n        throw new ExerciseNotCompletedException(); //todo\n    }\n\n    /**\n     * Returns a number of occurrences of the particular character.\n     *\n     * @param character a specific character\n     * @return a number that shows how many times this character appeared in a text file\n     */\n    public int getCharCount(char character) {\n        throw new ExerciseNotCompletedException(); //todo\n    }\n\n    /**\n     * Returns a character that appeared most often in the text.\n     *\n     * @return the most frequently appeared character\n     */\n    public char getMostPopularCharacter() {\n        throw new ExerciseNotCompletedException(); //todo\n    }\n\n    /**\n     * Returns {@code true} if this character has appeared in the text, and {@code false} otherwise\n     *\n     * @param character a specific character to check\n     * @return {@code true} if this character has appeared in the text, and {@code false} otherwise\n     */\n    public boolean containsCharacter(char character) {\n        throw new ExerciseNotCompletedException(); //todo\n    }\n}\n"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/main/java/com/bobocode/se/FileStatsException.java",
    "content": "package com.bobocode.se;\n\npublic class FileStatsException extends RuntimeException{\n    public FileStatsException(String message) {\n        super(message);\n    }\n\n    public FileStatsException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/test/java/com/bobocode/se/FileStatsTest.java",
    "content": "package com.bobocode.se;\n\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\nimport static org.assertj.core.api.Assertions.*;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class FileStatsTest {\n\n    @Test\n    @Order(1)\n    void createFileStatsFromExistingFile() {\n        FileStats fileStats = FileStats.from(\"sotl.txt\");\n    }\n\n    @Test\n    @Order(2)\n    void createFileStatsFromNonExistingFile() {\n        assertThatThrownBy(() -> FileStats.from(\"blahblah.txt\")).isInstanceOf(FileStatsException.class);\n    }\n\n    @Test\n    @Order(3)\n    void getCharCount() {\n        FileStats lambdaArticleFileStats = FileStats.from(\"sotl.txt\");\n        FileStats springCloudArticleFileStats = FileStats.from(\"scosb.txt\");\n\n        int aCharCountInLambdaArticle = lambdaArticleFileStats.getCharCount('a');\n        int bCharCountInSpringArticle = springCloudArticleFileStats.getCharCount('b');\n\n        assertThat(aCharCountInLambdaArticle).isEqualTo(2345);\n        assertThat(bCharCountInSpringArticle).isEqualTo(4);\n    }\n\n    @Test\n    @Order(4)\n    void getMostPopularCharacter() {\n        FileStats lambdaArticleFileStats = FileStats.from(\"sotl.txt\");\n        FileStats springCloudArticleFileStats = FileStats.from(\"scosb.txt\");\n\n        char mostPopularCharacterInLambdaArticle = lambdaArticleFileStats.getMostPopularCharacter();\n        char mostPopularCharacterInSpringArticle = springCloudArticleFileStats.getMostPopularCharacter();\n\n        System.out.println(mostPopularCharacterInSpringArticle);\n\n        assertThat(mostPopularCharacterInLambdaArticle).isEqualTo('e');\n        assertThat(mostPopularCharacterInSpringArticle).isEqualTo('e');\n    }\n\n    @Test\n    @Order(5)\n    void containsCharacter() {\n        FileStats lambdaArticleFileStats = FileStats.from(\"sotl.txt\");\n        FileStats springCloudArticleFileStats = FileStats.from(\"scosb.txt\");\n\n        boolean lambdaArticleContainsExistingCharacter = lambdaArticleFileStats.containsCharacter('a');\n        boolean lambdaArticleContainsWhitespace = lambdaArticleFileStats.containsCharacter(' ');\n        boolean springArticleContainsExistingCharacter = springCloudArticleFileStats.containsCharacter('b');\n        boolean springArticleContainsWhitespace = springCloudArticleFileStats.containsCharacter(' ');\n\n        assertThat(lambdaArticleContainsExistingCharacter).isTrue();\n        assertThat(lambdaArticleContainsWhitespace).isFalse();\n        assertThat(springArticleContainsExistingCharacter).isTrue();\n        assertThat(springArticleContainsWhitespace).isFalse();\n    }\n}"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/test/resources/scosb.txt",
    "content": "We’re pleased to announce that the 2.0.1 release of Spring Cloud Open Service Broker is now available. This release resolves a few issues that were raised since the 2.0.0 release. Thank you to the community for your interest and feedback!\n\nSpring Cloud Open Service Broker is a framework for building Spring Boot applications that implement the Open Service Broker API. The Open Service Broker API project allows developers to deliver services to applications running within cloud native platforms such as Cloud Foundry, Kubernetes, and OpenShift."
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/test/resources/sotl.txt",
    "content": "State of the Lambda\nSeptember 2013\nJava SE 8 Edition\nThis is an informal overview of the enhancements to the Java programming language specified by JSR 335 and implemented in the OpenJDK Lambda Project. It refines the previous iteration posted in December 2011. A formal description of some of the language changes may be found in the Early Draft Specification for the JSR; an OpenJDK Developer Preview is also available. Additional historical design documents can be found at the OpenJDK project page. There is also a companion document, State of the Lambda, Libraries Edition, describing the library enhancements added as part of JSR 335.\n\nThe high-level goal of Project Lambda is to enable programming patterns that require modeling code as data to be convenient and idiomatic in Java. The principal new language features include:\n\nLambda expressions (informally, \"closures\" or \"anonymous methods\")\nMethod and constructor references\nExpanded target typing and type inference\nDefault and static methods in interfaces\nThese are described and illustrated below.\n\n1. Background\nJava is, primarily, an object-oriented programming language. In both object-oriented and functional languages, basic values can dynamically encapsulate program behavior: object-oriented languages have objects with methods, and functional languages have functions. This similarity may not be obvious, however, because Java objects tend to be relatively heavyweight: instantiations of separately-declared classes wrapping a handful of fields and many methods.\n\nYet it is common for some objects to essentially encode nothing more than a function. In a typical use case, a Java API defines an interface, sometimes described as a \"callback interface,\" expecting the user to provide an instance of the interface when invoking the API. For example:\n\npublic interface ActionListener { \n    void actionPerformed(ActionEvent e);\n}\nRather than declaring a class that implements ActionListener for the sole purpose of allocating it once at an invocation site, a user typically instantiates the implementing class inline, anonymously:\n\nbutton.addActionListener(new ActionListener() { \n  public void actionPerformed(ActionEvent e) { \n    ui.dazzle(e.getModifiers());\n  }\n});\nMany useful libraries rely on this pattern. It is particularly important for parallel APIs, in which the code to execute must be expressed independently of the thread in which it will run. The parallel-programming domain is of special interest, because as Moore's Law continues to give us more cores but not faster cores, serial APIs are limited to a shrinking fraction of available processing power.\n\nGiven the increasing relevance of callbacks and other functional-style idioms, it is important that modeling code as data in Java be as lightweight as possible. In this respect, anonymous inner classes are imperfect for a number of reasons, primarily:\n\nBulky syntax\nConfusion surrounding the meaning of names and this\nInflexible class-loading and instance-creation semantics\nInability to capture non-final local variables\nInability to abstract over control flow\nThis project addresses many of these issues. It eliminates (1) and (2) by introducing new, much more concise expression forms with local scoping rules, sidesteps (3) by defining the semantics of the new expressions in a more flexible, optimization-friendly manner, and ameliorates (4) by allowing the compiler to infer finality (allowing capture of effectively final local variables).\n\nHowever, it is not a goal of this project to address all the problems of inner classes. Neither arbitrary capture of mutable variables (4) nor nonlocal control flow (5) are within this project's scope (though such features may be revisited in a future iteration of the language.)\n\n2. Functional interfaces\nThe anonymous inner class approach, despite its limitations, has the nice property of fitting very cleanly into Java's type system: a function value with an interface type. This is convenient for a number of reasons: interfaces are already an intrinsic part of the type system; they naturally have a runtime representation; and they carry with them informal contracts expressed by Javadoc comments, such as an assertion that an operation is commutative.\n\nThe interface ActionListener, used above, has just one method. Many common callback interfaces have this property, such as Runnable and Comparator. We'll give all interfaces that have just one method a name: functional interfaces. (These were previously called SAM Types, which stood for \"Single Abstract Method\".)\n\nNothing special needs to be done to declare an interface as functional; the compiler identifies it as such based on its structure. (This identification process is a little more than just counting method declarations; an interface might redundantly declare a method that is automatically provided by the class Object, such as toString(), or might declare static or default methods, none of which count against the one-method limit.) However, API authors may additionally capture the design intent that an interface be functional (as opposed to accidentally having only one method) with the @FunctionalInterface annotation, in which case the compiler will validate that the interface meets the structural requirements to be a functional interface.\n\nAn alternative (or complementary) approach to function types, suggested by some early proposals, would have been to introduce a new, structural function type, sometimes called arrow types. A type like \"function from a String and an Object to an int\" might be expressed as (String,Object)->int. This idea was considered and rejected, at least for now, due to several disadvantages:\n\nIt would add complexity to the type system and further mix structural and nominal types (Java is almost entirely nominally typed).\nIt would lead to a divergence of library styles -- some libraries would continue to use callback interfaces, while others would use structural function types.\nThe syntax could be unwieldy, especially when checked exceptions were included.\nIt is unlikely that there would be a runtime representation for each distinct function type, meaning developers would be further exposed to and limited by erasure. For example, it would not be possible (perhaps surprisingly) to overload methods m(T->U) and m(X->Y).\nSo, we have instead followed the path of \"use what you know\" -- since existing libraries use functional interfaces extensively, we codify and leverage this pattern. This enables existing libraries to be used with lambda expressions.\n\nTo illustrate, here is a sampling of some of the functional interfaces already in Java SE 7 that are well-suited for being used with the new language features; the examples that follow illustrate the use of a few of them.\n\njava.lang.Runnable\njava.util.concurrent.Callable\njava.security.PrivilegedAction\njava.util.Comparator\njava.io.FileFilter\njava.beans.PropertyChangeListener\nIn addition, Java SE 8 adds a new package, java.util.function, which contains functional interfaces that are expected to be commonly used, such as:\n\nPredicate<T> -- a boolean-valued property of an object\nConsumer<T> -- an action to be performed on an object\nFunction<T,R> -- a function transforming a T to a R\nSupplier<T> -- provide an instance of a T (such as a factory)\nUnaryOperator<T> -- a function from T to T\nBinaryOperator<T> -- a function from (T, T) to T\nIn addition to these basic \"shapes\", there are also primitive specializations such as IntSupplier or LongBinaryOperator. (Rather than provide the full complement of primitive specializations, we provide only specializations for int, long, and double; the other primitive types can be accomodated through conversions.) Similarly, there are some specializations for multiple arities, such as BiFunction<T,U,R>, which represents a function from (T,U) to R.\n\n3. Lambda expressions\nThe biggest pain point for anonymous classes is bulkiness. They have what we might call a \"vertical problem\": the ActionListener instance from section 1 uses five lines of source code to encapsulate a single aspect of behavior.\n\nLambda expressions are anonymous methods, aimed at addressing the \"vertical problem\" by replacing the machinery of anonymous inner classes with a lighter-weight mechanism.\n\nHere are some examples of lambda expressions:\n\n(int x, int y) -> x + y\n\n() -> 42\n\n(String s) -> { System.out.println(s); }\nThe first expression takes two integer arguments, named x and y, and returns their sum. The second takes no arguments and returns the integer 42. The third takes a string and prints it to the console, returning nothing.\n\nThe general syntax consists of an argument list, the arrow token ->, and a body. The body can either be a single expression, or a statement block. In the expression form, the body is simply evaluated and returned. In the block form, the body is evaluated like a method body -- a return statement returns control to the caller of the anonymous method; break and continue are illegal at the top level, but are of course permitted within loops; and if the body produces a result, every control path must return something or throw an exception.\n\nThe syntax is optimized for the common case in which a lambda expression is quite small, as illustrated above. For example, the expression-body form eliminates the need for a return keyword, which could otherwise represent a substantial syntactic overhead relative to the size of the expression.\n\nIt is also expected that lambda expressions will frequently appear in nested contexts, such as the argument to a method invocation or the result of another lambda expression. To minimize noise in these cases, unnecessary delimiters are avoided. However, for situations in which it is useful to set the entire expression apart, it can be surrounded with parentheses, just like any other expression.\n\nHere are some examples of lambda expressions appearing in statements:\n\nFileFilter java = (File f) -> f.getName().endsWith(\".java\");\n\nString user = doPrivileged(() -> System.getProperty(\"user.name\"));\n\nnew Thread(() -> {\n  connectToService();\n  sendNotification();\n}).start();\n4. Target typing\nNote that the name of a functional interface is not part of the lambda expression syntax. So what kind of object does a lambda expression represent? Its type is inferred from the surrounding context. For example, the following lambda expression is an ActionListener:\n\nActionListener l = (ActionEvent e) -> ui.dazzle(e.getModifiers());\nAn implication of this approach is that the same lambda expression can have different types in different contexts:\n\nCallable<String> c = () -> \"done\";\n\nPrivilegedAction<String> a = () -> \"done\";\nIn the first case, the lambda expression () -> \"done\" represents an instance of Callable. In the second case, the same expression represents an instance of PrivilegedAction.\n\nThe compiler is responsible for inferring the type of each lambda expression. It uses the type expected in the context in which the expression appears; this type is called the target type. A lambda expression can only appear in a context whose target type is a functional interface.\n\nOf course, no lambda expression will be compatible with every possible target type. The compiler checks that the types used by the lambda expression are consistent with the target type's method signature. That is, a lambda expression can be assigned to a target type T if all of the following conditions hold:\n\nT is a functional interface type\nThe lambda expression has the same number of parameters as T's method, and those parameters' types are the same\nEach expression returned by the lambda body is compatible with T's method's return type\nEach exception thrown by the lambda body is allowed by T's method's throws clause\nSince a functional interface target type already \"knows\" what types the lambda expression's formal parameters should have, it is often unnecessary to repeat them. The use of target typing enables the lambda parameters' types to be inferred:\n\nComparator<String> c = (s1, s2) -> s1.compareToIgnoreCase(s2);\nHere, the compiler infers that the type of s1 and s2 is String. In addition, when there is just one parameter whose type is inferred (a very common case), the parentheses surrounding a single parameter name are optional:\n\nFileFilter java = f -> f.getName().endsWith(\".java\");\n\nbutton.addActionListener(e -> ui.dazzle(e.getModifiers()));\nThese enhancements further a desirable design goal: \"Don't turn a vertical problem into a horizontal problem.\" We want the reader of the code to have to wade through as little syntax as possible before arriving at the \"meat\" of the lambda expression.\n\nLambda expressions are not the first Java expressions to have context-dependent types: generic method invocations and \"diamond\" constructor invocations, for example, are similarly type-checked based on an assignment's target type.\n\nList<String> ls = Collections.emptyList();\nList<Integer> li = Collections.emptyList();\n\nMap<String,Integer> m1 = new HashMap<>();\nMap<Integer,String> m2 = new HashMap<>();\n5. Contexts for target typing\nWe stated earlier that lambda expressions can only appear in contexts that have target types. The following contexts have target types:\n\nVariable declarations\nAssignments\nReturn statements\nArray initializers\nMethod or constructor arguments\nLambda expression bodies\nConditional expressions (?:)\nCast expressions\nIn the first three cases, the target type is simply the type being assigned to or returned.\n\nComparator<String> c;\nc = (String s1, String s2) -> s1.compareToIgnoreCase(s2);\n\npublic Runnable toDoLater() {\n  return () -> {\n    System.out.println(\"later\");\n  };\n}\nArray initializer contexts are like assignments, except that the \"variable\" is an array component and its type is derived from the array's type.\n\nfilterFiles(new FileFilter[] { \n               f -> f.exists(), f -> f.canRead(), f -> f.getName().startsWith(\"q\") \n            });\nIn the method argument case, things are more complicated: target type determination interacts with two other language features, overload resolution and type argument inference.\n\nOverload resolution involves finding the best method declaration for a particular method invocation. Since different declarations have different signatures, this can impact the target type of a lambda expression used as an argument. The compiler will use what it knows about the lambda expression to make this choice. If a lambda expression is explicitly typed (specifies the types of its parameters), the compiler will know not only the parameter types but also the type of all return expressions in its body. If the lambda is implicitly typed (inferred parameter types), overload resolution will ignore the lambda body and only use the number of lambda parameters.\n\nIf the choice of a best method declaration is ambiguous, casts or explicit lambdas can provide additional type information for the compiler to disambiguate. If the return type targeted by a lambda expression depends on type argument inference, then the lambda body may provide information to the compiler to help infer the type arguments.\n\nList<Person> ps = ...\nString<String> names = ps.stream().map(p -> p.getName());\nHere, ps is a List<Person>, so ps.stream() is a Stream<Person>. The map() method is generic in R, where the parameter of map() is a Function<T,R>, where T is the stream element type. (T is known to be Person at this point.) Once the overload is selected and the lambda's target type is known, we need to infer R; we do this by type-checking the lambda body, and discovering that its return type is String, and hence R is String, and therefore the map() expression has a type of Stream<String>. Most of the time, the compiler just figures this all out, but if it gets stuck, we can provide additional type information via an explicit lambda (give the argument p an explicit type), casting the lambda to an explicit target type such as Function<Person,String>, or providing an explicit type witness for the generic parameter R (.<String>map(p -> p.getName())).\n\nLambda expressions themselves provide target types for their bodies, in this case by deriving that type from the outer target type. This makes it convenient to write functions that return other functions:\n\nSupplier<Runnable> c = () -> () -> { System.out.println(\"hi\"); };\nSimilarly, conditional expressions can \"pass down\" a target type from the surrounding context:\n\nCallable<Integer> c = flag ? (() -> 23) : (() -> 42);\nFinally, cast expressions provide a mechanism to explicitly provide a lambda expression's type if none can be conveniently inferred from context:\n\n// Illegal: Object o = () -> { System.out.println(\"hi\"); };\nObject o = (Runnable) () -> { System.out.println(\"hi\"); };\nCasts are also useful to help resolve ambiguity when a method declaration is overloaded with unrelated functional interface types.\n\nThe expanded role of target typing in the compiler is not limited to lambda expressions: generic method invocations and \"diamond\" constructor invocations can also take advantage of target types wherever they are available. The following declarations are illegal in Java SE 7 but valid in Java SE 8:\n\nList<String> ls =\n  Collections.checkedList(new ArrayList<>(), String.class);\n\nSet<Integer> si = flag ? Collections.singleton(23)\n                       : Collections.emptySet();\n6. Lexical scoping\nDetermining the meaning of names (and this) in inner classes is significantly more difficult and error-prone than when classes are limited to the top level. Inherited members -- including methods of class Object -- can accidentally shadow outer declarations, and unqualified references to this always refer to the inner class itself.\n\nLambda expressions are much simpler: they do not inherit any names from a supertype, nor do they introduce a new level of scoping. Instead, they are lexically scoped, meaning names in the body are interpreted just as they are in the enclosing environment (with the addition of new names for the lambda expression's formal parameters). As a natural extension, the this keyword and references to its members have the same meaning as they would immediately outside the lambda expression.\n\nTo illustrate, the following program prints \"Hello, world!\" twice to the console:\n\npublic class Hello {\n  Runnable r1 = () -> { System.out.println(this); }\n  Runnable r2 = () -> { System.out.println(toString()); }\n\n  public String toString() { return \"Hello, world!\"; }\n\n  public static void main(String... args) {\n    new Hello().r1.run();\n    new Hello().r2.run();\n  }\n}\nThe equivalent using anonymous inner classes would instead, perhaps to the programmer's surprise, print something like Hello$1@5b89a773 and Hello$2@537a7706.\n\nConsistent with the lexical-scoping approach, and following the pattern set by other local parameterized constructs like for loops and catch clauses, the parameters of a lambda expression must not shadow any local variables in the enclosing context.\n\n7. Variable capture\nThe compiler check for references to local variables of enclosing contexts in inner classes (captured variables) is quite restrictive in Java SE 7: an error occurs if the captured variable is not declared final. We relax this restriction -- for both lambda expressions and inner classes -- by also allowing the capture of effectively final local variables.\n\nInformally, a local variable is effectively final if its initial value is never changed -- in other words, declaring it final would not cause a compilation failure.\n\nCallable<String> helloCallable(String name) {\n  String hello = \"Hello\";\n  return () -> (hello + \", \" + name);\n}\nReferences to this -- including implicit references through unqualified field references or method invocations -- are, essentially, references to a final local variable. Lambda bodies that contain such references capture the appropriate instance of this. In other cases, no reference to this is retained by the object.\n\nThis has a beneficial implication for memory management: while inner class instances always hold a strong reference to their enclosing instance, lambdas that do not capture members from the enclosing instance do not hold a reference to it. This characteristic of inner class instances can often be a source of memory leaks.\n\nWhile we relax the syntactic restrictions on captured values, we still prohibit capture of mutable local variables. The reason is that idioms like this:\n\nint sum = 0;\nlist.forEach(e -> { sum += e.size(); }); // ERROR\nare fundamentally serial; it is quite difficult to write lambda bodies like this that do not have race conditions. Unless we are willing to enforce -- preferably at compile time -- that such a function cannot escape its capturing thread, this feature may well cause more trouble than it solves. Lambda expressions close over values, not variables.\n\nAnother reason to not support capture of mutable variables is that there's a better way to address accumulation problems without mutation, and instead treat this problem as a reduction. The java.util.stream package provides both general and specialized (such as sum, min, and max) reductions on collections and other data structures. For example, instead of using forEach and mutation, we could do a reduction which is safe both sequentially or in parallel:\n\nint sum = list.stream()\n              .mapToInt(e -> e.size())\n              .sum();\nThe sum() method is provided for convenience, but is equivalent to the more general form of reduction:\n\nint sum = list.stream()\n              .mapToInt(e -> e.size())\n              .reduce(0, (x,y) -> x+y);\nReduction takes a base value (in case the input is empty) and an operator (here, addition), and computes the following expression:\n\n0 + list[0] + list[1] + list[2] + ...\nReduction can be done with other operations as well, such as minimum, maximum, product, etc, and if the operator is associative, is easily and safely parallelized. So, rather than supporting an idiom that is fundamentally sequential and prone to data races (mutable accumulators), we instead choose to provide library support to express accumulations in a more parallelizable and less error-prone way.\n\n8. Method references\nLambda expressions allow us to define an anonymous method and treat it as an instance of a functional interface. It is often desirable to do the same with an existing method.\n\nMethod references are expressions which have the same treatment as lambda expressions (i.e., they require a target type and encode functional interface instances), but instead of providing a method body, they refer an existing method by name.\n\nFor example, consider a Person class that can be sorted by name or by age.\n\nclass Person { \n    private final String name;\n    private final int age;\n\n    public int getAge() { return age; }\n    public String getName() { return name; }\n   ...\n}\n\nPerson[] people = ...\nComparator<Person> byName = Comparator.comparing(p -> p.getName());\nArrays.sort(people, byName);\nWe can rewrite this to use a method reference to Person.getName() instead:\n\nComparator<Person> byName = Comparator.comparing(Person::getName);\nHere, the expression Person::getName can be considered shorthand for a lambda expression which simply invokes the named method with its arguments, and returns the result. While the method reference may not (in this case) be any more syntactically compact, it is clearer -- the method that we want to call has a name, and so we can refer to it directly by name.\n\nBecause the functional interface method's parameter types act as arguments in an implicit method invocation, the referenced method signature is allowed to manipulate the parameters -- via widening, boxing, grouping as a variable-arity array, etc. -- just like a method invocation.\n\nConsumer<Integer> b1 = System::exit;   // void exit(int status)\nConsumer<String[]> b2 = Arrays::sort;  // void sort(Object[] a)\nConsumer<String> b3 = MyProgram::main; // void main(String... args)\nRunnable r = MyProgram::main;          // void main(String... args)\n9. Kinds of method references\nThere are several different kinds of method references, each with slightly different syntax:\n\nA static method (ClassName::methName)\nAn instance method of a particular object (instanceRef::methName)\nA super method of a particular object (super::methName)\nAn instance method of an arbitrary object of a particular type (ClassName::methName)\nA class constructor reference (ClassName::new)\nAn array constructor reference (TypeName[]::new)\nFor a static method reference, the class to which the method belongs precedes the :: delimiter, such as in Integer::sum.\n\nFor a reference to an instance method of a particular object, an expression evaluating to an object reference precedes the delimiter:\n\nSet<String> knownNames = ...\nPredicate<String> isKnown = knownNames::contains;\nHere, the implicit lambda expression would capture the String object referred to by knownNames, and the body would invoke Set.contains using that object as the receiver.\n\nThe ability to reference the method of a specific object provides a convenient way to convert between different functional interface types:\n\nCallable<Path> c = ...\nPrivilegedAction<Path> a = c::call;\nFor a reference to an instance method of an arbitrary object, the type to which the method belongs precedes the delimiter, and the invocation's receiver is the first parameter of the functional interface method:\n\nFunction<String, String> upperfier = String::toUpperCase;\nHere, the implicit lambda expression has one parameter, the string to be converted to upper case, which becomes the receiver of the invocation of the toUpperCase() method.\n\nIf the class of the instance method is generic, its type parameters can be provided before the :: delimiter or, in most cases, inferred from the target type.\n\nNote that the syntax for a static method reference might also be interpreted as a reference to an instance method of a class. The compiler determines which is intended by attempting to identify an applicable method of each kind (noting that the instance method has one less argument).\n\nFor all forms of method references, method type arguments are inferred as necessary, or they can be explicitly provided following the :: delimiter.\n\nConstructors can be referenced in much the same was as static methods by using the name new:\n\nSocketImplFactory factory = MySocketImpl::new;\nIf a class has multiple constructors, the target type's method signature is used to select the best match in the same way that a constructor invocation is resolved.\n\nFor inner classes, no syntax supports explicitly providing an enclosing instance parameter at the site of the constructor reference.\n\nIf the class to instantiate is generic, type arguments can be provided after the class name, or they are inferred as for a \"diamond\" constructor invocation.\n\nThere is a special syntactic form of constructor references for arrays, which treats arrays as if they had a constructor that accepts an int parameter. For example:\n\nIntFunction<int[]> arrayMaker = int[]::new;\nint[] array = arrayMaker.apply(10);  // creates an int[10]\n10. Default and static interface methods\nLambda expressions and method references add a lot of expressiveness to the Java language, but the key to really achieving our goal of making code-as-data patterns convenient and idiomatic is to complement these new features with libraries tailored to take advantage of them.\n\nAdding new functionality to existing libraries is somewhat difficult in Java SE 7. In particular, interfaces are essentially set in stone once they are published; unless one can update all possible implementations of an interface simultaneously, adding a new method to an interface can cause existing implementations to break. The purpose of default methods (previously referred to as virtual extension methods or defender methods) is to enable interfaces to be evolved in a compatible manner after their initial publication.\n\nTo illustrate, the standard collections API obviously ought to provide new lambda-friendly operations. For example, the removeAll method could be generalized to remove any of a collection's elements for which an arbitrary property held, where the property was expressed as an instance of a functional interface Predicate. But where would this new method be defined? We can't add an abstract method to the Collection interface -- many existing implementations wouldn't know about the change. We could make it a static method in the Collections utility class, but that would relegate these new operations to a sort of second-class status.\n\nDefault methods provide a more object-oriented way to add concrete behavior to an interface. These are a new kind of method: interface method can either be abstract or default. Default methods have an implementation that is inherited by classes that do not override it (see the next section for the details). Default methods in a functional interface don't count against its limit of one abstract method. For example, we could have (though did not) add a skip method to Iterator, as follows:\n\ninterface Iterator<E> {\n    boolean hasNext();\n    E next();\n    void remove();\n\n    default void skip(int i) {\n        for (; i > 0 && hasNext(); i--) next();\n    }\n}\nGiven the above definition of Iterator, all classes that implement Iterator would inherit a skip method. From a client's perspective, skip is just another virtual method provided by the interface. Invoking skip on an instance of a subclass of Iterator that does not provide a body for skip has the effect of invoking the default implementation: calling hasNext and next up to a certain number of times. If a class wants to override skip with a better implementation -- by advancing a private cursor directly, for example, or incorporating an atomicity guarantee -- it is free to do so.\n\nWhen one interface extends another, it can add a default to an inherited abstract method, provide a new default for an inherited default method, or reabstract a default method by redeclaring the method as abstract.\n\nIn addition to allowing code in interfaces in the form of default methods, Java SE 8 also introduces the ability to place static methods in interfaces as well. This allows helper methods that are specific to an interface to live with the interface, rather than in a side class (which is often named for the plural of the interface). For example, Comparator acquired static helper methods for making comparators, which takes a function that extracts a Comparable sort key and produces a Comparator:\n\npublic static <T, U extends Comparable<? super U>> \nComparator<T> comparing(Function<T, U> keyExtractor) {\n    return (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));\n}\n11. Inheritance of default methods\nDefault methods are inherited just like other methods; in most cases, the behavior is just as one would expect. However, when a class's or interface's supertypes provide multiple methods with the same signature, the inheritance rules attempt to resolve the conflict. Two basic principles drive these rules:\n\nClass method declarations are preferred to interface defaults. This is true whether the class method is concrete or abstract. (Hence the default keyword: default methods are a fallback if the class hierarchy doesn't say anything.)\n\nMethods that are already overridden by other candidates are ignored. This circumstance can arise when supertypes share a common ancestor.\n\nAs an example of how the second rule comes into play, say the Collection and List interfaces provided different defaults for removeAll, and Queue inherits the default method from Collection; in the following implements clause, the List declaration would have priority over the Collection declaration inherited by Queue:\n\nclass LinkedList<E> implements List<E>, Queue<E> { ... }\nIn the event that two independently-defined defaults conflict, or a default method conflicts with an abstract method, it is a compilation error. In this case, the programmer must explicitly override the supertype methods. Often, this amounts to picking the preferred default, and declaring a body that invokes the preferred default. An enhanced syntax for super supports the invocation of a particular superinterface's default implementation:\n\ninterface Robot implements Artist, Gun {\n    default void draw() { Artist.super.draw(); }\n}\nThe name preceding super must refer to a direct superinterface that defines or inherits a default for the invoked method. This form of method invocation is not restricted to simple disambiguation -- it can be used just like any other invocation, in both classes and interfaces.\n\nIn no case does the order in which interfaces are declared in an inherits or extends clause, or which interface was implemented \"first\" or \"more recently\", affect inheritance.\n\n12. Putting it together\nThe language and library features for Project Lambda were designed to work together. To illustrate, we'll consider the task of sorting a list of people by last name.\n\nToday we write:\n\nList<Person> people = ...\nCollections.sort(people, new Comparator<Person>() {\n    public int compare(Person x, Person y) {\n        return x.getLastName().compareTo(y.getLastName());\n    }\n});\nThis is a very verbose way to write \"sort people by last name\"!\n\nWith lambda expressions, we can make this expression more concise:\n\nCollections.sort(people, \n                 (Person x, Person y) -> x.getLastName().compareTo(y.getLastName()));\nHowever, while more concise, it is not any more abstract; it still burdens the programmer with the need to do the actual comparison (which is even worse when the sort key is a primitive). Small changes to the libraries can help here, such the static comparing method added to Comparator:\n\nCollections.sort(people, Comparator.comparing((Person p) -> p.getLastName()));\nThis can be shortened by allowing the compiler to infer the type of the lambda parameter, and importing the comparing method via a static import:\n\nCollections.sort(people, comparing(p -> p.getLastName()));\nThe lambda in the above expression is simply a forwarder for the existing method getLastName. We can use method references to reuse the existing method in place of the lambda expression:\n\nCollections.sort(people, comparing(Person::getLastName));\nFinally, the use of an ancillary method like Collections.sort is undesirable for many reasons: it is more verbose; it can't be specialized for each data structure that implements List; and it undermines the value of the List interface since users can't easily discover the static sort method when inspecting the documentation for List.\n\nDefault methods provide a more object-oriented solution for this problem, where we've added a sort() method to List:\n\npeople.sort(comparing(Person::getLastName));\nWhich also reads much more like to the problem statement in the first place: sort the people list by last name.\n\nIf we add a default method reversed() to Comparator, which produces a Comparator that uses the same sort key but in reverse order, we can just as easily express a descending sort:\n\npeople.sort(comparing(Person::getLastName).reversed());\n13. Summary\nJava SE 8 adds a relatively small number of new language features -- lambda expressions, method references, default and static methods in interfaces, and more widespread use of type inference. Taken together, though, they enable programmers to express their intent more clearly and concisely with less boilerplate, and enable the development of more powerful, parallel-friendly libraries."
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Crazy Regex\n\n### Pre-conditions ❗\nYou're supposed to know how to work regex and be able to build Patterns and Matchers\n\n### Objectives\n* **build Patterns to extract** necessary parts from text ✅\n* **manipulate** extracted text with **Matcher** object ✅\n\n### Regular expressions - sequence of characters that define a search pattern for text\n\n---\n\nThere 2 peace pf puzzle:\n* Literal characters - I want to match literally the character I specified (like 'a')\n* Meta characters - I want to match any character of this kind (more generic/abstract thing)\n\nSingle char\n\n* \\\\d -> 0-9                  \n* \\\\D -> negate of \\\\d             \n* \\\\w -> A-Za-z0-9        \n* \\\\W -> negate of \\\\w                           \n* \\\\s -> whitespace, tab  \n* \\\\S -> negate of \\\\s                         \n* .  -> anything but newline              \n* \\\\. -> literal dot                       \n\n\nQuantifiers - modify single characters how many of them you want match in a row\n* \\* -> Occurs zero or more times           \n* \\+ -> 1 or more                           \n* ? -> zero or one                         \n* {min, max} -> some range\n* {n} -> precise quantity\n\n\nPosition\n*   ^  -> beginning\n*   $  -> end\n*  \\\\b -> word boundary \n\n---\n\nCharacter class -> is the thing that appears in between []. For example [abc] -> match 'a' or 'b' or 'c'.\nAnother example [-.] -> match dash or period. Here . is not meta character anymore and ^ are special characters inside []\n* [0-5] -> match all numbers  from 0 to 5. [^0-5] -> match anything that NOT 0-5\nBUT it works like meta character only when it on first position, otherwise - its literal, [a^bc] - like this\n\n---\n\nCapturing Groups - whenever u do regex search it matches whole result as a group 0.\n* \\\\d{3}-\\\\d{3}-\\\\d{4}  ->  212-555-1234 = GROUP 0\n\nParentheses can capture a subgroup:\n\\\\d{3}-(\\\\d{3})-(\\\\d{4}) where 212-555-1234 = GROUP 0, 555 = GROUP 1, 1234 = GROUP 2\n\nWe can refer to this groups by $1 ($ when we want to replace) and \\1 (within regex itself referring to capture group \nit's called back reference)\n\n---\n\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>3-0-java-core</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>3-6-3-crazy-regex</artifactId>\n\n\n</project>"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/src/main/java/com/bobocode/se/CrazyRegex.java",
    "content": "package com.bobocode.se;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.regex.Pattern;\n\n/**\n * {@link CrazyRegex} is an exercise class. Each method returns Pattern class which\n * should be created using regex expression. Every method that is not implemented yet\n * throws {@link ExerciseNotCompletedException}\n * <p>\n * TODO: remove exception and implement each method of this class using {@link Pattern}\n *\n * @author Andriy Paliychuk\n */\npublic class CrazyRegex {\n\n    /**\n     * A Pattern that that finds all words \"Curiosity\" in text\n     *\n     * @return a pattern that looks for the word \"Curiosity\"\n     */\n    public Pattern findSpecificWord() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds first word in text\n     *\n     * @return a pattern that looks for the first word in text\n     */\n    public Pattern findFirstWord() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds last word in text\n     *\n     * @return a pattern that looks for the last word in text\n     */\n    public Pattern findLastWord() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all numbers in text. When we have \"555-555\", \"(555)555\" and \"30th\" in text\n     * our pattern must grab all that numbers:\n     * \"555\" - four times, and one \"30\"\n     *\n     * @return a pattern that looks for numbers\n     */\n    public Pattern findAllNumbers() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all dates. For instance: \"1971-11-23\"\n     *\n     * @return a pattern that looks for dates\n     */\n    public Pattern findDates() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds different variations of word \"color\".\n     * We are looking for: \"color\", \"colour\", \"colors\", \"colours\"\n     *\n     * @return a pattern that looks for different variations of word \"color\"\n     */\n    public Pattern findDifferentSpellingsOfColor() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all zip codes in text.\n     * Zip code is a 5-digit number without any characters or special symbols.\n     * For example: 72300\n     *\n     * @return a pattern that looks for zip codes\n     */\n    public Pattern findZipCodes() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds different variations of word \"link\".\n     * We are looking for: \"lynk\", \"link\", \"l nk\", \"l(nk\"\n     *\n     * @return a pattern that looks for different variations of word \"link\"\n     */\n    public Pattern findDifferentSpellingsOfLink() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds phone numbers.\n     * For example: \"555-555-5555\"\n     *\n     * @return a pattern that looks for phone numbers\n     */\n    public Pattern findSimplePhoneNumber() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds numbers with following requirements:\n     * - inside the number can be only digits from 0 to 5\n     * - length 3\n     *\n     * @return a pattern that looks for numbers with length 3 and digits from 0 to 5 in the middle\n     */\n    public Pattern findNumbersFromZeroToFiveWithLengthThree() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all words in text that have length 5\n     *\n     * @return a pattern that looks for the words that have length 5\n     */\n    public Pattern findAllWordsWithFiveLength() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds words and numbers with following constraints:\n     * - not shorter than two symbols\n     * - not longer than three symbols\n     *\n     * @return a pattern that looks for words and numbers that not shorter 2 and not longer 3\n     */\n    public Pattern findAllLettersAndDigitsWithLengthThree() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all words that begin with capital letter\n     *\n     * @return a pattern that looks for the words that begin with capital letter\n     */\n    public Pattern findAllWordsWhichBeginWithCapitalLetter() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds only the following abbreviation:\n     * - AK, AL, AR, AZ, CA, CO, CT, PR, PA, PD\n     *\n     * @return a pattern that looks for the abbreviations above\n     */\n    public Pattern findAbbreviation() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all open braces\n     *\n     * @return a pattern that looks for all open braces\n     */\n    public Pattern findAllOpenBraces() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds everything inside []\n     *\n     * @return a pattern that looks for everything inside []\n     */\n    public Pattern findOnlyResources() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all https links in note.txt\n     *\n     * @return a pattern that looks for all https links in note.txt\n     */\n    public Pattern findOnlyLinksInNote() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all http links in nasa.json\n     *\n     * @return a pattern that looks for all http links in nasa.json\n     */\n    public Pattern findOnlyLinksInJson() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds all .com, .net and .edu emails\n     *\n     * @return a pattern that looks for all .com, .net and .edu emails\n     */\n    public Pattern findAllEmails() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds the following examples of phone numbers:\n     * -  555-555-5555\n     * -  555.555.5555\n     * -  (555)555-5555\n     *\n     * @return a pattern that looks for phone numbers patterns above\n     */\n    public Pattern findAllPatternsForPhoneNumbers() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * A Pattern that finds only duplicates\n     *\n     * @return a pattern that looks for duplicates\n     */\n    public Pattern findOnlyDuplicates() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * You have a text where all names recorded as first name, last name.\n     * Create matcher and use method replaceAll to record that names as:\n     * - last name first name\n     *\n     * @return String where all names recorded as last name first name\n     */\n    public String replaceFirstAndLastNames(String names) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * You have a text with phone numbers.\n     * Create matcher and use method replaceAll to replace last digits:\n     * -  555-XXX-XXXX\n     *\n     * @return String where in all phone numbers last 7 digits replaced to X\n     */\n    public String replaceLastSevenDigitsOfPhoneNumberToX(String phones) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * You have a text with resources and links to those resources:\n     * - [Bobocode](https://www.bobocode.com)\n     * Create matcher and use method replaceAll to get the following result:\n     * - <a href=\"https://www.bobocode.com\">Bobocode</a>\n     *\n     * @return String where all resources embraced in href\n     */\n    public String insertLinksAndResourcesIntoHref(String links) {\n        throw new ExerciseNotCompletedException();\n    }\n}\n"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/src/test/java/com/bobocode/se/CrazyRegexTest.java",
    "content": "package com.bobocode.se;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\n\n/**\n * A test class for {@link CrazyRegex}.\n *\n * @author Andriy Paliychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class CrazyRegexTest {\n\n    private final CrazyRegex crazyRegex = new CrazyRegex();\n\n    private final String text;\n    private final String json;\n\n    public CrazyRegexTest() {\n        this.text = readWholeFile(\"note.txt\");\n        this.json = readWholeFile(\"nasa.json\");\n    }\n\n    @Test\n    @Order(1)\n    void findSpecificWord() {\n        String result = regexChecker(crazyRegex.findSpecificWord(), json);\n        assertThat(result).isEqualTo(\"\\nCuriosity\\nCuriosity\\nCuriosity\");\n    }\n\n    @Test\n    @Order(2)\n    void findFirstWord() {\n        String result = regexChecker(crazyRegex.findFirstWord(), text);\n        assertThat(result).isEqualTo(\"\\nThe\");\n    }\n\n    @Test\n    @Order(3)\n    void findLastWord() {\n        String result = regexChecker(crazyRegex.findLastWord(), text);\n        assertThat(result).isEqualTo(\"\\nfish\");\n    }\n\n    @Test\n    @Order(4)\n    void findAllNumbers() {\n        String result = regexChecker(crazyRegex.findAllNumbers(), text);\n        assertThat(result).isEqualTo(\"\\n01001\\n03148\\n02132\\n412\\n555\\n1212\\n412\\n555\" +\n                                     \"\\n1234\\n412\\n555\\n1234\\n646\\n555\\n1234\\n1\");\n    }\n\n    @Test\n    @Order(5)\n    void findDates() {\n        String result = regexChecker(crazyRegex.findDates(), json);\n        assertThat(result).isEqualTo(\"\\n2015-05-30\\n2012-08-06\\n2011-11-26\\n2015-05-30\\n2012-08-06\\n\" +\n                                     \"2011-11-26\\n2015-05-30\\n2012-08-06\\n2011-11-26\");\n    }\n\n    @Test\n    @Order(6)\n    void findDifferentSpellingsOfColor() {\n        String result = regexChecker(crazyRegex.findDifferentSpellingsOfColor(), text);\n        assertThat(result).isEqualTo(\"\\ncolors\\ncolours\\ncolour\");\n    }\n\n    @Test\n    @Order(7)\n    void findZipCodes() {\n        String result = regexChecker(crazyRegex.findZipCodes(), text);\n        assertThat(result).isEqualTo(\"\\n 01001 \\n 03148 \\n 02132 \");\n    }\n\n    @Test\n    @Order(8)\n    void findDifferentSpellingsOfLink() {\n        String result = regexChecker(crazyRegex.findDifferentSpellingsOfLink(), text);\n        assertThat(result).isEqualTo(\"\\nlynk\\nlink\\nl nk\\nl(nk\");\n    }\n\n    @Test\n    @Order(9)\n    void findSimplePhoneNumber() {\n        String result = regexChecker(crazyRegex.findSimplePhoneNumber(), text);\n        assertThat(result).isEqualTo(\"\\n412-555-1234\");\n    }\n\n    @Test\n    @Order(10)\n    void findNumbersFromZeroToFiveWithLengthThree() {\n        String result = regexChecker(crazyRegex.findNumbersFromZeroToFiveWithLengthThree(), text);\n        assertThat(result).isEqualTo(\"\\n010\\n031\\n021\\n412\\n555\\n121\\n412\" +\n                                     \"\\n555\\n123\\n412\\n555\\n123\\n555\\n123\");\n    }\n\n    @Test\n    @Order(11)\n    void findAllWordsWithFiveLength() {\n        String result = regexChecker(crazyRegex.findAllWordsWithFiveLength(), json);\n        assertThat(result).isEqualTo(\"\\nFront\\nrover\\nFront\\nrover\\nrover\");\n    }\n\n    @Test\n    @Order(12)\n    void findAllLettersAndDigitsWithLengthThree() {\n        String result = regexChecker(crazyRegex.findAllLettersAndDigitsWithLengthThree(), text);\n        assertThat(result).isEqualTo(\"\\nThe\\nof\\nthe\\nand\\nthe\\nnot\\nThe\\nis\\ndon\\nyou\\nnk\\nnk\\nThe\\nCA\\nAK\\nPA\\n412\" +\n                                     \"\\n555\\ncom\\n412\\n555\\n412\\n555\\n646\\n555\\nof\\ncom\\nnet\\nor\\nnyu\\nedu\\n1Z\\naaa\\nOf\\nwww\\ncom\\ncom\\nwww\\ncom\" +\n                                     \"\\nis\\nis\\nam\\nnot\\nnot\\nwhy\\nwhy\\nam\\nok\\ncat\\ncat\\ndog\\ndog\");\n    }\n\n    @Test\n    @Order(13)\n    void findAllWordsWhichBeginWithCapitalLetter() {\n        String result = regexChecker(crazyRegex.findAllWordsWhichBeginWithCapitalLetter(), json);\n        assertThat(result).isEqualTo(\"\\nFront\\nHazard\\nAvoidance\\nCamera\" +\n                                     \"\\nCuriosity\\nFront\\nHazard\\nAvoidance\\nCamera\\nCuriosity\\nRear\\nHazard\\nAvoidance\\nCamera\\nCuriosity\");\n    }\n\n    @Test\n    @Order(14)\n    void findAbbreviation() {\n        String result = regexChecker(crazyRegex.findAbbreviation(), text);\n        assertThat(result).isEqualTo(\"\\nCA\\nAK\\nPA\");\n    }\n\n    @Test\n    @Order(15)\n    void findAllOpenBraces() {\n        String result = regexChecker(crazyRegex.findAllOpenBraces(), text);\n        assertThat(result).isEqualTo(\"\\n{{{\\n{{\\n{\");\n    }\n\n    @Test\n    @Order(16)\n    void findOnlyResources() {\n        String result = regexChecker(crazyRegex.findOnlyResources(), text);\n        assertThat(result).isEqualTo(\"\\nGoogle\\nStackOverflow\\nYoutube\");\n    }\n\n    @Test\n    @Order(17)\n    void findOnlyLinksInNote() {\n        String result = regexChecker(crazyRegex.findOnlyLinksInNote(), text);\n        assertThat(result).isEqualTo(\"\\nhttps://www.google.com\\nhttps://stackoverflow.com\\nhttps://www.youtube.com\");\n    }\n\n    @Test\n    @Order(18)\n    void findOnlyLinksInJson() {\n        String result = regexChecker(crazyRegex.findOnlyLinksInJson(), json);\n        assertThat(result).isEqualTo(\n                \"\\nhttp://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01000/opgs/edr/fcam/FLB_486265257EDR_F0481570FHAZ00323M_.JPG\\n\" +\n                \"http://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01000/opgs/edr/fcam/FRB_486265257EDR_F0481570FHAZ00323M_.JPG\\n\" +\n                \"http://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01000/opgs/edr/rcam/RLB_486265291EDR_F0481570RHAZ00323M_.JPG\"\n        );\n    }\n\n    @Test\n    @Order(19)\n    void findAllEmails() {\n        String result = regexChecker(crazyRegex.findAllEmails(), text);\n        assertThat(result).isEqualTo(\"\\njohnsmith@yahoo.com\\nterek.koval@gmail.com\\nterek@koval.net\" +\n                                     \"\\nterek.koval@nyu.edu\");\n    }\n\n    @Test\n    @Order(20)\n    void findAllPatternsForPhoneNumbers() {\n        String result = regexChecker(crazyRegex.findAllPatternsForPhoneNumbers(), text);\n        assertThat(result).isEqualTo(\"\\n(412)555-1212\\n412-555-1234\\n646.555.1234\");\n    }\n\n    @Test\n    @Order(21)\n    void findOnlyDuplicates() {\n        String result = regexChecker(crazyRegex.findOnlyDuplicates(), text);\n        assertThat(result).isEqualTo(\"\\nis is\\ntext text\\ndouble double\\nI I\\nnot not\\nwhy why\" +\n                                     \"\\ncat cat\\ndog\\ndog\\nfish fish\");\n    }\n\n    @Test\n    @Order(22)\n    void replaceFirstAndLastNames() {\n        String names = \"Tarasenko, Nazar ... Petrashyk, Petro ... Zlepko, Andrii\";\n        String result = crazyRegex.replaceFirstAndLastNames(names);\n        assertThat(result).isEqualTo(\"Nazar Tarasenko ... Petro Petrashyk ... Andrii Zlepko\");\n    }\n\n    @Test\n    @Order(23)\n    void replaceLastSevenDigitsOfPhoneNumberToX() {\n        String phones = \"(948)333-5656 1235-889-7897 111.747.6236\";\n        String result = crazyRegex.replaceLastSevenDigitsOfPhoneNumberToX(phones);\n        assertThat(result).isEqualTo(\"948-XXX-XXXX 1235-XXX-XXXX 111-XXX-XXXX\");\n    }\n\n    @Test\n    @Order(24)\n    void insertLinksAndResourcesIntoHref() {\n        String links = \"[Bobocode](https://www.bobocode.com)\" +\n                       \"\\n[LinkedIn](https://www.linkedin.com)\" +\n                       \"\\n[Netflix](https://www.netflix.com)\";\n        String result = crazyRegex.insertLinksAndResourcesIntoHref(links);\n        assertThat(result).isEqualTo(\n                \"<a href=\\\"https://www.bobocode.com\\\">Bobocode</a>\\n\" +\n                \"<a href=\\\"https://www.linkedin.com\\\">LinkedIn</a>\\n\" +\n                \"<a href=\\\"https://www.netflix.com\\\">Netflix</a>\"\n        );\n    }\n\n    private String regexChecker(Pattern pattern, String str2WorkWith) {\n        Matcher matcher = pattern.matcher(str2WorkWith);\n        StringBuilder stringBuilder = new StringBuilder();\n        while (matcher.find()) {\n            if (matcher.group().length() != 0) {\n                stringBuilder.append(\"\\n\").append(matcher.group());\n            }\n        }\n        return stringBuilder.toString();\n    }\n\n    @SneakyThrows\n    private String readWholeFile(String fileName) {\n        Path filePath = Paths.get(CrazyRegex.class.getClassLoader()\n                .getResource(fileName)\n                .toURI());\n        try (Stream<String> fileLinesStream = Files.lines(filePath)) {\n            return fileLinesStream.collect(joining(\"\\n\"));\n        }\n    }\n}"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/src/test/resources/nasa.json",
    "content": "{\"photos\":[\n  {\"id\":102693,\n    \"sol\":1000,\n    \"camera\":{\"id\":20,\"name\":\"FHAZ\",\"rover_id\":5,\"full_name\":\"Front Hazard Avoidance Camera\"},\n    \"img_src\":\"http://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01000/opgs/edr/fcam/FLB_486265257EDR_F0481570FHAZ00323M_.JPG\",\n    \"earth_date\":\"2015-05-30\",\n    \"rover\":{\"id\":5,\"name\":\"Curiosity\",\"landing_date\":\"2012-08-06\",\"launch_date\":\"2011-11-26\",\"status\":\"active\"}\n  },\n  {\"id\":102694,\n    \"sol\":1000,\n    \"camera\":{\"id\":20,\"name\":\"FHAZ\",\"rover_id\":5,\"full_name\":\"Front Hazard Avoidance Camera\"},\n    \"img_src\":\"http://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01000/opgs/edr/fcam/FRB_486265257EDR_F0481570FHAZ00323M_.JPG\",\n    \"earth_date\":\"2015-05-30\",\n    \"rover\":{\"id\":5,\"name\":\"Curiosity\",\"landing_date\":\"2012-08-06\",\"launch_date\":\"2011-11-26\",\"status\":\"active\"}\n  },\n  {\"id\":102850,\n    \"sol\":1000,\n    \"camera\":{\"id\":21,\"name\":\"RHAZ\",\"rover_id\":5,\"full_name\":\"Rear Hazard Avoidance Camera\"},\n    \"img_src\":\"http://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01000/opgs/edr/rcam/RLB_486265291EDR_F0481570RHAZ00323M_.JPG\",\n    \"earth_date\":\"2015-05-30\",\n    \"rover\":{\"id\":5,\"name\":\"Curiosity\",\"landing_date\":\"2012-08-06\",\"launch_date\":\"2011-11-26\",\"status\":\"active\"}\n  }\n]}"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/src/test/resources/note.txt",
    "content": "The colors of the rainbow have many colours and the rainbow does not have a single colour\nThe lynk is quite a link don't you think? l nk l(nk\nThe Collin Richardson CA 01001 AK 03148 PA 02132 (412)555-1212 johnsmith@yahoo.com 412-555-1234 412 555-1234 646.555.1234\nI know email addresses of fascinating people like terek.koval@gmail.com terek@koval.net or\nterek.koval@nyu.edu\n  1Z aaa **** *** {{{ {{ { Of\n[Google](https://www.google.com)[StackOverflow](https://stackoverflow.com)[Youtube](https://www.youtube.com)\nThis is is some text text with double double words some where I I I am not not sure why why I am typing ok? cat cat dog\ndog fish fish"
  },
  {
    "path": "3-0-java-core/3-6-4-random-field-comparator/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Random Field Comparator\n#### Improve your reflection-related skills implementing a random field comparator 💪\n\n### Objectives\n* implement a logic of choosing a random field to use it for comparison of objects of provided type ✅\n* implement a mechanism to check if field type is `Comparable` ✅\n* implement a method `compare` that compares two objects by randomly-provided field ✅\n* extend a method `compare` to manage null field values following condition when null value greater than a non-null value ✅\n* implement method `getComparingFieldName` that retrieves the name of randomly-chosen comparing field✅\n* implement method `toString` ✅\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "3-0-java-core/3-6-4-random-field-comparator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>3-0-java-core</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>3-6-4-random-field-comparator</artifactId>\n\n\n</project>"
  },
  {
    "path": "3-0-java-core/3-6-4-random-field-comparator/src/main/java/com/bobocode/se/RandomFieldComparator.java",
    "content": "package com.bobocode.se;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\nimport java.util.Comparator;\n\n/**\n * A generic comparator that is comparing a random field of the given class. The field is either primitive or\n * {@link Comparable}. It is chosen during comparator instance creation and is used for all comparisons.\n * <p>\n * If no field is available to compare, the constructor throws {@link IllegalArgumentException}\n *\n * @param <T> the type of the objects that may be compared by this comparator\n *<p><p>\n *  <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n *  <p>\n *\n * @author Stanislav Zabramnyi\n */\npublic class RandomFieldComparator<T> implements Comparator<T> {\n\n    public RandomFieldComparator(Class<T> targetType) {\n        throw new ExerciseNotCompletedException(); // todo: implement this constructor;\n    }\n\n    /**\n     * Compares two objects of the class T by the value of the field that was randomly chosen. It allows null values\n     * for the fields, and it treats null value greater than a non-null value.\n     *\n     * @param o1\n     * @param o2\n     * @return positive int in case of first parameter {@param o1} is greater than second one {@param o2},\n     *         zero if objects are equals,\n     *         negative int in case of first parameter {@param o1} is less than second one {@param o2}.\n     */\n    @Override\n    public int compare(T o1, T o2) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method;\n    }\n\n    /**\n     * Returns the name of the randomly-chosen comparing field.\n     */\n    public String getComparingFieldName() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method;\n    }\n\n    /**\n     * Returns a statement \"Random field comparator of class '%s' is comparing '%s'\" where the first param is the name\n     * of the type T, and the second parameter is the comparing field name.\n     *\n     * @return a predefined statement\n     */\n    @Override\n    public String toString() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method;\n    }\n}\n"
  },
  {
    "path": "3-0-java-core/3-6-4-random-field-comparator/src/test/java/com/bobocode/se/RandomFieldComparatorTest.java",
    "content": "package com.bobocode.se;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport lombok.AllArgsConstructor;\nimport lombok.NoArgsConstructor;\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\n@TestMethodOrder(OrderAnnotation.class)\nclass RandomFieldComparatorTest {\n\n    private final RandomFieldComparator<Account> randomFieldComparator = new RandomFieldComparator<>(Account.class);\n\n    @Test\n    @Order(1)\n    @DisplayName(\"Constructor throws an exception when parameter is null\")\n    void classDoesNotApplyNullInConstructor() {\n        assertThrows(NullPointerException.class, () -> new RandomFieldComparator<>(null));\n    }\n\n    @Test\n    @Order(2)\n    @SneakyThrows\n    @DisplayName(\"Constructor throws an exception when the target type has no Comparable fields\")\n    void constructorThrowsExceptionIfNoComparableFieldsInProvidedType() {\n        assertThrows(IllegalArgumentException.class, () -> new RandomFieldComparator<>(ClassWithNotComparableField.class));\n    }\n\n    @Test\n    @Order(3)\n    @DisplayName(\"Method 'compare' throws an exception when any parameter is null\")\n    void compareWhenFirstParameterAreNull() {\n\n        assertThrows(NullPointerException.class, () -> randomFieldComparator.compare(null, new Account()));\n        assertThrows(NullPointerException.class, () -> randomFieldComparator.compare(new Account(), null));\n    }\n\n    @Test\n    @Order(4)\n    @DisplayName(\"Method 'compare' returns 0 when field values of both objects are null\")\n    void compareWhenBothFieldValuesIsNull() {\n        setFieldToCompare(\"lastName\", Account.class);\n        int compareResult = randomFieldComparator.compare(new Account(), new Account());\n\n        assertThat(compareResult).isZero();\n    }\n\n    @Test\n    @Order(5)\n    @DisplayName(\"Method compare returns positive int when the first field value is null\")\n    void compareWhenFieldValuesOfFirstObjectIsNull() {\n        Account emptyAccount = new Account();\n        Account account = new Account(\"Sibma\", \"LoinKing\", \"simba-bimba@gmail.com\", 14);\n        setFieldToCompare(\"email\", Account.class);//set field to compare explicitly as there are int field which has default value 0\n        int compareResult = randomFieldComparator.compare(emptyAccount, account);\n\n        assertThat(compareResult).isPositive();\n    }\n\n    @Test\n    @Order(6)\n    @DisplayName(\"Method compare returns negative int when the second field value is null\")\n    void compareWhenFieldValuesOfSecondObjectIsNull() {\n        Account account = new Account(\"Mufasa\", \"LoinKing\", \"simba-bimba@gmail.com\", 47);\n        Account emptyAccount = new Account();\n        setFieldToCompare(\"firstName\", Account.class);\n        int compareResult = randomFieldComparator.compare(account, emptyAccount);\n\n        assertThat(compareResult).isNegative();\n    }\n\n    @Test\n    @Order(7)\n    @SneakyThrows\n    @DisplayName(\"Method 'compare' returns positive int when the first value is greater\")\n    void compareWhenFieldValueOfFirstObjectIsGreater() {\n        var fieldToCompareName = \"firstName\";\n        Account account1 = new Account();\n        Account account2 = new Account();\n        Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName);\n        fieldToCompareAccount.setAccessible(true);\n\n        fieldToCompareAccount.set(account1, \"Bob\");\n        fieldToCompareAccount.set(account2, \"Alice\");\n\n        setFieldToCompare(fieldToCompareName, Account.class);\n        int compareResult = randomFieldComparator.compare(account1, account2);\n\n        assertThat(compareResult).isPositive();\n    }\n\n    @Test\n    @Order(8)\n    @SneakyThrows\n    @DisplayName(\"Method 'compare' returns negative int when the first value is smaller\")\n    void compareWhenFieldValueOfSecondObjectIsGreater() {\n        var fieldToCompareName = \"firstName\";\n        Account account1 = new Account();\n        Account account2 = new Account();\n        Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName);\n        fieldToCompareAccount.setAccessible(true);\n\n        fieldToCompareAccount.set(account1, \"Alice\");\n        fieldToCompareAccount.set(account2, \"Bob\");\n\n        setFieldToCompare(fieldToCompareName, Account.class);\n        int compareResult = randomFieldComparator.compare(account1, account2);\n\n        assertThat(compareResult).isNegative();\n    }\n\n    @Test\n    @Order(9)\n    @SneakyThrows\n    @DisplayName(\"Method 'compare' returns zero when the field values are equal\")\n    void compareWhenFieldValuesOfObjectsAreEqual() {\n        var fieldToCompareName = \"firstName\";\n        Account account1 = new Account();\n        Account account2 = new Account();\n        Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName);\n        fieldToCompareAccount.setAccessible(true);\n\n        fieldToCompareAccount.set(account1, \"Carol\");\n        fieldToCompareAccount.set(account2, \"Carol\");\n\n        setFieldToCompare(fieldToCompareName, Account.class);\n        int compareResult = randomFieldComparator.compare(account1, account2);\n\n        assertThat(compareResult).isZero();\n    }\n\n    @Test\n    @Order(10)\n    @SneakyThrows\n    @DisplayName(\"Method 'compare' returns positive int when the first primitive value is greater\")\n    void comparePrimitivesWhenFieldValueOfFirstObjectIsGreater() {\n        var fieldToCompareName = \"age\";\n        Account account1 = new Account();\n        Account account2 = new Account();\n        Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName);\n        fieldToCompareAccount.setAccessible(true);\n\n        fieldToCompareAccount.setInt(account1, 7);\n        fieldToCompareAccount.setInt(account2, 3);\n\n        setFieldToCompare(fieldToCompareName, Account.class);\n        int compareResult = randomFieldComparator.compare(account1, account2);\n\n        assertThat(compareResult).isPositive();\n    }\n\n    @Test\n    @Order(11)\n    @SneakyThrows\n    @DisplayName(\"Method 'compare' returns zero when the primitive field values are equal\")\n    void comparePrimitivesWhenFieldValuesOfObjectsAreEqual() {\n        var fieldToCompareName = \"age\";\n        Account account1 = new Account();\n        Account account2 = new Account();\n        Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName);\n        fieldToCompareAccount.setAccessible(true);\n\n        fieldToCompareAccount.setInt(account1, 15);\n        fieldToCompareAccount.setInt(account2, 15);\n\n        setFieldToCompare(fieldToCompareName, Account.class);\n        int compareResult = randomFieldComparator.compare(account1, account2);\n\n        assertThat(compareResult).isZero();\n    }\n\n    @Test\n    @Order(12)\n    @SneakyThrows\n    @DisplayName(\"Method 'compare' returns negative int when the first primitive value is smaller\")\n    void comparePrimitivesWhenFieldValueOfSecondObjectIsGreater() {\n        var fieldToCompareName = \"age\";\n        Account account1 = new Account();\n        Account account2 = new Account();\n        Field fieldToCompareAccount = account1.getClass().getDeclaredField(fieldToCompareName);\n        fieldToCompareAccount.setAccessible(true);\n\n        fieldToCompareAccount.setInt(account1, 4);\n        fieldToCompareAccount.setInt(account2, 8);\n\n        setFieldToCompare(fieldToCompareName, Account.class);\n        int compareResult = randomFieldComparator.compare(account1, account2);\n\n        assertThat(compareResult).isNegative();\n    }\n\n    @Test\n    @Order(13)\n    @SneakyThrows\n    @DisplayName(\"Method 'getComparingFieldName' returns the name of randomly-chosen field to be compared\")\n    void getComparingFieldName() {\n        var fieldToCompareName = \"lastName\";\n        setFieldToCompare(fieldToCompareName, Account.class);\n\n        assertEquals(fieldToCompareName, randomFieldComparator.getComparingFieldName());\n    }\n\n    @Test\n    @Order(14)\n    @SneakyThrows\n    @DisplayName(\"Method toString is properly overridden\")\n    void toStringOverriding() {\n        var expectedString = \"Random field comparator of class 'Account' is comparing 'email'\";\n        var fieldToCompareName = \"email\";\n        setFieldToCompare(fieldToCompareName, Account.class);\n\n        assertEquals(expectedString, randomFieldComparator.toString());\n    }\n\n    @SneakyThrows\n    private <T> void setFieldToCompare(String fieldName, Class<T> classType) {\n        Field fieldToCompare = Arrays.stream(randomFieldComparator.getClass().getDeclaredFields())\n                .filter(f -> f.getType().equals(Field.class))\n                .findAny()\n                .orElseThrow();\n        fieldToCompare.setAccessible(true);\n        fieldToCompare.set(randomFieldComparator, classType.getDeclaredField(fieldName));\n    }\n\n    private static class EmptyClass {\n\n    }\n\n    @AllArgsConstructor\n    private static class ClassWithNotComparableField {\n\n        private Object field;\n    }\n\n    @NoArgsConstructor\n    @AllArgsConstructor\n    private static class Account {\n\n        private String firstName;\n        private String lastName;\n        private String email;\n        private int age;\n    }\n}"
  },
  {
    "path": "3-0-java-core/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Java Core\nBuild strong core skills needed for enterprise Java development"
  },
  {
    "path": "3-0-java-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>com.bobocode</groupId>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <artifactId>3-0-java-core</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>3-6-1-file-reader</module>\n        <module>3-6-2-file-stats</module>\n        <module>3-6-3-crazy-regex</module>\n        <module>3-6-4-random-field-comparator</module>\n    </modules>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>java-fundamentals-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n            <scope>compile</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Flight search\nImprove your OOP skills and learn the power on polymorphism by inverting the compile-time dependency 💪\n\n### Task\n`FlighService` and package `service` represent a component of the system that holds a business logic. `FlightDao` \nand package `data` represent a component of the system that implements data access layer. Your job is to implement \nthe *todo* section of those classes **following OOP design principles.**\n \n### Pre-conditions ❗️\nYou're supposed to be familiar polymorphism and Java interfaces\n\n### Objectives\n* **implement `FlightDao`** (data access object) methods using a simple `HashMap` ✅\n* **implement service method** that allows to **register flight** using DAO ✅\n* **implement service method** that allows **search for a flight** using DAO ✅\n* **invert the dependency** between service and DAO by adding an interface ✅\n\n### Related materials ℹ️\n * [SOLID](https://en.wikipedia.org/wiki/SOLID)\n\n---\n\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>4-0-object-oriented-programming</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>4-3-1-flight-search</artifactId>\n\n\n</project>"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/data/FlightDao.java",
    "content": "package com.bobocode.oop.data;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * {@link FlightDao} represents a Data Access Object (DAO) for flights. The implementation is simplified, so it just\n * uses {@link HashSet} to store flight numbers.\n * <p>\n * todo: 1. Implement a method {@link FlightDao#register(String)} that store new flight number into the set\n * todo: 2. Implement a method {@link FlightDao#findAll()} that returns a set of all flight numbers\n */\npublic class FlightDao {\n    private Set<String> flights = new HashSet<>();\n\n    /**\n     * Stores a new flight number\n     *\n     * @param flightNumber a flight number to store\n     * @return {@code true} if a flight number was stored, {@code false} otherwise\n     */\n    public boolean register(String flightNumber) {\n        throw new ExerciseNotCompletedException();// todo: implement this method\n    }\n\n    /**\n     * Returns all stored flight numbers\n     *\n     * @return a set of flight numbers\n     */\n    public Set<String> findAll() {\n        throw new ExerciseNotCompletedException();// todo: implement this method\n    }\n\n}\n"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/factory/FlightServiceFactory.java",
    "content": "package com.bobocode.oop.factory;\n\nimport com.bobocode.oop.service.FlightService;\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link FlightServiceFactory} is used to create an instance of {@link FlightService}\n * <p>\n * todo: 1. Implement method {@link FlightServiceFactory#creteFlightService()}\n */\npublic class FlightServiceFactory {\n\n    /**\n     * Create a new instance of {@link FlightService}\n     *\n     * @return FlightService\n     */\n    public FlightService creteFlightService() {\n        throw new ExerciseNotCompletedException();\n    }\n}\n"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/service/FlightService.java",
    "content": "package com.bobocode.oop.service;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.List;\n\n/**\n * {@link FlightService} provides an API that allows to manage flight numbers\n * <p>\n * todo: 1. Using {@link com.bobocode.oop.data.FlightDao} implement method {@link FlightService#registerFlight(String)}\n * todo: 2. Using {@link com.bobocode.oop.data.FlightDao} implement method {@link FlightService#searchFlights(String)}\n */\npublic class FlightService {\n\n    /**\n     * Adds a new flight number\n     *\n     * @param flightNumber a flight number to add\n     * @return {@code true} if a flight number was added, {@code false} otherwise\n     */\n    public boolean registerFlight(String flightNumber) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns all flight numbers that contains a provided key.\n     *\n     * @param query a search query\n     * @return a list of found flight numbers\n     */\n    public List<String> searchFlights(String query) {\n        throw new ExerciseNotCompletedException();\n    }\n}\n"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/src/test/java/com/bobocode/oop/FlightServiceTest.java",
    "content": "package com.bobocode.oop;\n\nimport com.bobocode.oop.factory.FlightServiceFactory;\nimport com.bobocode.oop.service.FlightService;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\npublic class FlightServiceTest {\n\n    private FlightService flightService = new FlightServiceFactory().creteFlightService();\n\n    @Test\n    public void testRegisterFlight() {\n        boolean registered = flightService.registerFlight(\"PR344\");\n\n        assertTrue(registered);\n    }\n\n    @Test\n    public void testRegisterSameFlightTwice() {\n        flightService.registerFlight(\"RB122\");\n\n        boolean registeredSecondTime = flightService.registerFlight(\"RB122\");\n\n        assertFalse(registeredSecondTime);\n    }\n\n    @Test\n    public void testSearchExistingFlightByFullNumber() {\n        flightService.registerFlight(\"OL234\");\n        flightService.registerFlight(\"KM23234\");\n        flightService.registerFlight(\"LTE114\");\n        flightService.registerFlight(\"BRT14\");\n\n        List<String> foundFlights = flightService.searchFlights(\"LTE114\");\n\n        assertEquals(1, foundFlights.size());\n        assertEquals(\"LTE114\", foundFlights.get(0));\n    }\n\n    @Test\n    public void testSearchNonExistingFlight() {\n        List<String> foundFlights = flightService.searchFlights(\"XXX\");\n\n        assertNotNull(foundFlights);\n    }\n\n    @Test\n    public void testSearchFlights() {\n        flightService.registerFlight(\"OR1214\");\n        flightService.registerFlight(\"BTR14\");\n        flightService.registerFlight(\"BMK198\");\n        flightService.registerFlight(\"RLR198\");\n\n        List<String> foundFlights = flightService.searchFlights(\"R1\");\n\n        assertTrue(foundFlights.contains(\"OR1214\"));\n        assertTrue(foundFlights.contains(\"BTR14\"));\n        assertTrue(foundFlights.contains(\"RLR198\"));\n        assertEquals(3, foundFlights.size());\n    }\n}\n"
  },
  {
    "path": "4-0-object-oriented-programming/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Java Object-Oriented Programming\nBuild strong object-oriented programming skills needed for enterprise Java development 💪"
  },
  {
    "path": "4-0-object-oriented-programming/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>pom</packaging>\n    <modules>\n        <module>4-3-1-flight-search</module>\n    </modules>\n\n    <parent>\n        <groupId>com.bobocode</groupId>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <artifactId>4-0-object-oriented-programming</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>java-fundamentals-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n            <scope>compile</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Lambda Functions Map\nStart learning functional programming in Java by writing simple math functions using Lambdas 💪\n\n### Objectives\n\n* implement **abs** (absolute) function using lambda ✅\n* implement **sgn** (signum) function using lambda ✅\n* implement **increment** function using lambda ✅\n* implement **decrement** function using lambda ✅\n* implement **square** function using lambda ✅\n* add all those functions to the function map ✅\n\n---\n\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>5-0-functional-programming</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>5-0-1-lambda-functions-map</artifactId>\n\n\n</project>"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/FunctionMap.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.fp.exception.InvalidFunctionNameException;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\n/**\n * {@link FunctionMap} is an API that allows you to store and retrieve functions by string name. {@link FunctionMap}\n * is stored in a {@link HashMap}, where the key is a function name, and the value is a {@link Function} instance.\n *\n * @author Taras Boychuk\n */\npublic class FunctionMap<T, R> {\n    private Map<String, Function<T, R>> functionMap;\n\n    FunctionMap() {\n        functionMap = new HashMap<>();\n    }\n\n    public void addFunction(String name, Function<T, R> function) {\n        functionMap.put(name, function);\n    }\n\n    public Function<T, R> getFunction(String name) {\n        if (functionMap.containsKey(name)) {\n            return functionMap.get(name);\n        } else {\n            throw new InvalidFunctionNameException(name);\n        }\n    }\n}"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/Functions.java",
    "content": "package com.bobocode.fp;\n\n/**\n * An util class that provides a factory method for creating an instance of a {@link FunctionMap} filled with a list\n * of functions.\n * <p>\n * TODO: implement a method and verify it by running {@link FunctionsTest}\n * <p>\n * TODO: if you find this exercise valuable and you want to get more like it, <a href=\"https://www.patreon.com/bobocode\"> \n *     please support us on Patreon</a>\n *\n * @author Taras Boychuk\n */\npublic class Functions {\n    private Functions() {\n    }\n\n    /**\n     * A static factory method that creates an integer function map with basic functions:\n     * - abs (absolute value)\n     * - sgn (signum function)\n     * - increment\n     * - decrement\n     * - square\n     *\n     * @return an instance of {@link FunctionMap} that contains all listed functions\n     */\n    public static FunctionMap<Integer, Integer> intFunctionMap() {\n        FunctionMap<Integer, Integer> intFunctionMap = new FunctionMap<>();\n\n        // todo: according to the javadoc add functions using lambda expression\n\n        return intFunctionMap;\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/exception/InvalidFunctionNameException.java",
    "content": "package com.bobocode.fp.exception;\n\npublic class InvalidFunctionNameException extends RuntimeException {\n    public InvalidFunctionNameException(String functionName) {\n        super(\"Function \" + functionName + \" doesn't exist.\");\n    }\n}"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/src/test/java/com/bobocode/fp/FunctionsTest.java",
    "content": "package com.bobocode.fp;\n\nimport org.junit.jupiter.api.*;\n\nimport java.util.function.Function;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class FunctionsTest {\n    private FunctionMap<Integer, Integer> integerFunctionMap;\n\n    @BeforeEach\n    public void init() {\n        integerFunctionMap = Functions.intFunctionMap();\n    }\n\n    @Test\n    @Order(7)\n    void squareFunction() {\n        Function<Integer, Integer> squareFunction = integerFunctionMap.getFunction(\"square\");\n\n        int result = squareFunction.apply(5);\n\n        assertThat(result).isEqualTo(25);\n    }\n\n    @Test\n    @Order(1)\n    void absFunction() {\n        Function<Integer, Integer> absFunction = integerFunctionMap.getFunction(\"abs\");\n\n        int result = absFunction.apply(-192);\n\n        assertThat(result).isEqualTo(192);\n    }\n\n    @Test\n    @Order(5)\n    void incrementFunction() {\n        Function<Integer, Integer> incrementFunction = integerFunctionMap.getFunction(\"increment\");\n\n        int result = incrementFunction.apply(399);\n\n        assertThat(result).isEqualTo(400);\n    }\n\n    @Test\n    @Order(6)\n    void destDecrementFunction() {\n        Function<Integer, Integer> decrementFunction = integerFunctionMap.getFunction(\"decrement\");\n\n        int result = decrementFunction.apply(800);\n\n        assertThat(result).isEqualTo(799);\n    }\n\n    @Test\n    @Order(2)\n    void signFunctionOnNegativeValue() {\n        Function<Integer, Integer> signFunction = integerFunctionMap.getFunction(\"sgn\");\n\n        int result = signFunction.apply(-123);\n\n        assertThat(result).isEqualTo(-1);\n    }\n\n    @Test\n    @Order(3)\n    void signFunctionOnPositiveValue() {\n        Function<Integer, Integer> signFunction = integerFunctionMap.getFunction(\"sgn\");\n\n        int result = signFunction.apply(23);\n\n        assertThat(result).isEqualTo(1);\n    }\n\n    @Test\n    @Order(4)\n    void signFunctionOnZero() {\n        Function<Integer, Integer> signFunction = integerFunctionMap.getFunction(\"sgn\");\n\n        int result = signFunction.apply(0);\n\n        assertThat(result).isEqualTo(0);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Stream sum of squares\nLearn functional programming by refactoring a piece of code using Stream API 💪\n\n### Objectives\n* **refactor imperative-style code** with for loop using Stream API ✅\n* **create a `IntStream`** based on given the first, and the last values ✅\n* **transform each element** of the stream into its square value ✅\n* **calculate a sum** of all elements in the stream ✅\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>5-0-functional-programming</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>5-0-2-stream-sum-of-squares</artifactId>\n\n\n</project>"
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/SumOfSquares.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.fp.exception.InvalidRangeException;\n\n/**\n * This class allow to calculate a sum of squares of integer number in a certain range. It was implemented using\n * OO approach. Your job is to refactor it using functional approach. E.g. avoid using mutable variables\n *\n * @author Taras Boychuk\n */\npublic class SumOfSquares {\n    public static void main(String[] args) {\n        System.out.println(\"Sum of squares from 5 to 10 is \" + calculateSumOfSquaresInRange(5, 10));\n    }\n\n    /**\n     * This method calculates the sum of squares of integer in the range\n     *\n     * @param startInclusive first element in range\n     * @param endInclusive   last element in range\n     * @return the sum of squares of each element in the range\n     */\n    static int calculateSumOfSquaresInRange(int startInclusive, int endInclusive) {\n        if (endInclusive < startInclusive) {\n            throw new InvalidRangeException();\n        }\n\n        // todo: refactor using functional approach – instead of using for loop, use IntStream.rangeClose()\n        int sumOfSquares = 0;\n        for (int i = startInclusive; i <= endInclusive; i++) {\n            sumOfSquares += i * i;\n        }\n        return sumOfSquares;\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/exception/InvalidRangeException.java",
    "content": "package com.bobocode.fp.exception;\n\npublic class InvalidRangeException extends RuntimeException {\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/src/test/java/com/bobocode/fp/SumOfSquaresTest.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.fp.exception.InvalidRangeException;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;\n\n/**\n * A test class for {@link SumOfSquares}\n *\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\nclass SumOfSquaresTest {\n\n    @Test\n    @Order(1)\n    void calculateSumOfSquaresOfZero() {\n        int sumOfSquares = SumOfSquares.calculateSumOfSquaresInRange(0, 0);\n\n        assertThat(sumOfSquares).isZero();\n    }\n\n    @Test\n    @Order(2)\n    void calculateSumOfSquaresOfOne() {\n        int sumOfSquares = SumOfSquares.calculateSumOfSquaresInRange(0, 1);\n\n        assertThat(sumOfSquares).isEqualTo(1);\n    }\n\n    @Test\n    @Order(3)\n    void calculateSumOfSquares() {\n        int sumOfSquares = SumOfSquares.calculateSumOfSquaresInRange(1, 5); // 1*1 + 2*2 + 3*3 + 4*4 + 5*5 = 55\n\n        assertThat(sumOfSquares).isEqualTo(55);\n    }\n\n    @Test\n    @Order(4)\n    void calculateSumOfSquaresOnNegative() {\n        int sumOfSquares = SumOfSquares.calculateSumOfSquaresInRange(-4, -2); // -4*(-4) + -3*(-3) + -2*(-2) = 29\n\n        assertThat(sumOfSquares).isEqualTo(29);\n    }\n\n    @Test\n    @Order(5)\n    void calculateWithInvalidRange() {\n        assertThatExceptionOfType(InvalidRangeException.class)\n                .isThrownBy(() -> SumOfSquares.calculateSumOfSquaresInRange(4, 1));\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-1-1-crazy-lambdas/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Crazy Lambdas\nBuild hard-core skills implementing crazy lambdas 💪\n\n### Tutorial \nJava is an OOP language, so it always works with classes and **doesn't support standalone functions**. \nIn case you want to **pass some function as a method parameter**, or **store some code into a variable**, \nyou should use a *Functional Interface* and a *Lambda expression*.\n\n* A *Functional Interface (FI)* represents a **function signature**. It contains only one abstract method.\n* A *Lambda expression* represents a **function body**. Is an anonymous function that implements the abstract method \n  of the functional interface\n\nThe purpose of the lambda and functional interfaces is to **make it easier to create function objects** \nand provide an **ability to use some functional programming technics in Java.**\n\nA typical example is interface `Comparator<T>`:\n\n```java\n        accounts.sort(new Comparator<Account>() {\n            @Override\n            public int compare(Account o1, Account o2) {\n                return o1.getFirstName().compareTo(o2.getFirstName());\n            }\n        });\n```\nIt can be easily simplified using lambda expression:\n```java\n        accounts.sort((a1, a2) -> a1.getFirstName().compareTo(a2.getFirstName()));\n```\nIn case you are calling some existing method inside the lambda, you can reference that method instead of actually \ncalling it. This technique is called *Method Reference*. Combining it with usefull default method `comparing()` \nit can help you to simplify the code even more:\n```java\n        accounts.sort(comparing(Account::getFirstName));\n```\n\n### Best practices\n* use **lambdas instead of anonymous classes**\n* **avoid lambda parameter types**, unless it can improve code readability\n* **keep lambda expression small** (1 line is the best option)\n* **always use `@FunctionalInterface` annotation** for custom functional interfaces\n* **prefer standard predefined functional interfaces** (`java.util.function`)\n* create a **custom FI**, in case it has some **specific contract**, and you can **benefit from self-descriptive name** and **default methods**\n* **use special FI for primitives** (e.g. `IntToDoubleFunction` instead of `Function<Integer, Double>`)\n* **prefer method reference** in all cases where it helps to improve readability\n\n### Related materials :information_source:\n* [State of lambda (JSR 335)](http://htmlpreview.github.io/?https://github.com/bobocode-projects/resources/blob/master/java8/lambda/sotl.html)\n* [Modern Java in Action](https://amzn.to/2KwUKW5) :green_book:\n    * Passing code with behaviour parameterization - Chapter 2\n    * Lambda expressions - Chapter 3\n* [Effective Java 3rd Edition](https://amzn.to/3mYA0U1) :blue_book:\n    * Prefer Lambdas to anonymous classes – Item 42\n    * Prefer method reference to lambdas - Item 43\n    * Favor the use of standard functional interfaces - Item 44\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/tNizKD1JbwM/0.jpg)](https://www.youtube.com/watch?v=tNizKD1JbwM)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "5-0-functional-programming/5-1-1-crazy-lambdas/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>5-0-functional-programming</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>5-1-1-crazy-lambdas</artifactId>\n\n\n</project>"
  },
  {
    "path": "5-0-functional-programming/5-1-1-crazy-lambdas/src/main/java/com/bobocode/fp/CrazyLambdas.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.math.BigDecimal;\nimport java.util.Comparator;\nimport java.util.Map;\nimport java.util.function.*;\n\n/**\n * {@link CrazyLambdas} is an exercise class. Each method returns a functional interface and it should be implemented\n * using either lambda or a method reference. Every method that is not implemented yet throws\n * {@link ExerciseNotCompletedException}.\n * <p>\n * TODO: remove exception and implement each method of this class using lambda or method reference\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @author Taras Boychuk\n */\npublic class CrazyLambdas {\n\n    /**\n     * Returns {@link Supplier} that always supply \"Hello\"\n     *\n     * @return a string supplier\n     */\n    public static Supplier<String> helloSupplier() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Predicate} of string that checks if string is empty\n     *\n     * @return a string predicate\n     */\n    public static Predicate<String> isEmptyPredicate() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Return a {@link Function} that accepts {@link String} and returns that string repeated n time, where n is passed\n     * as function argument\n     *\n     * @return function that repeats Strings\n     */\n    public static BiFunction<String, Integer, String> stringMultiplier() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Function} that converts a {@link BigDecimal} number into a {@link String} that start with\n     * a dollar sign and then gets a value\n     *\n     * @return function that converts adds dollar sign\n     */\n    public static Function<BigDecimal, String> toDollarStringFunction() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Receives two parameter that represent a range and returns a {@link Predicate<String>} that verifies if string\n     * length is in the specified range. E.g. min <= length < max\n     *\n     * @param min min length\n     * @param max max length\n     * @return a string predicate\n     */\n    public static Predicate<String> lengthInRangePredicate(int min, int max) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Supplier} of random integers\n     *\n     * @return int supplier\n     */\n    public static IntSupplier randomIntSupplier() {\n        throw new ExerciseNotCompletedException();\n    }\n\n\n    /**\n     * Returns an {@link IntUnaryOperator} that receives an int as a bound parameter, and returns a random int\n     *\n     * @return int operation\n     */\n    public static IntUnaryOperator boundedRandomIntSupplier() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns {@link IntUnaryOperator} that calculates an integer square\n     *\n     * @return square operation\n     */\n    public static IntUnaryOperator intSquareOperation() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link LongBinaryOperator} sum operation.\n     *\n     * @return binary sum operation\n     */\n    public static LongBinaryOperator longSumOperation() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link ToIntFunction<String>} that converts string to integer.\n     *\n     * @return string to int converter\n     */\n    public static ToIntFunction<String> stringToIntConverter() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Receives int parameter n, and returns a {@link Supplier} that supplies {@link IntUnaryOperator}\n     * that is a function f(x) = n * x\n     *\n     * @param n a multiplier\n     * @return a function supplier\n     */\n    public static Supplier<IntUnaryOperator> nMultiplyFunctionSupplier(int n) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link UnaryOperator} that accepts str to str function and returns the same function composed with trim\n     *\n     * @return function that composes functions with trim() function\n     */\n    public static UnaryOperator<Function<String, String>> composeWithTrimFunction() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Receives a {@link Runnable} parameter, and returns a {@link Supplier<Thread>}. The thread will be started only\n     * when you call supplier method {@link Supplier#get()}\n     *\n     * @param runnable the code you want to tun in new thread\n     * @return a thread supplier\n     */\n    public static Supplier<Thread> runningThreadSupplier(Runnable runnable) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Consumer} that accepts {@link Runnable} as a parameter and runs in a new thread.\n     *\n     * @return a runnable consumer\n     */\n    public static Consumer<Runnable> newThreadRunnableConsumer() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Function} that accepts an instance of {@link Runnable} and returns a {@link Supplier} of a\n     * started {@link Thread} that is created from a given {@link Runnable}\n     *\n     * @return a function that transforms runnable into a thread supplier\n     */\n    public static Function<Runnable, Supplier<Thread>> runnableToThreadSupplierFunction() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link BiFunction} that has two parameters. First is {@link IntUnaryOperator} which is some integer function.\n     * Second is {@link IntPredicate} which is some integer condition. And the third is {@link IntUnaryOperator} which is\n     * a new composed function that uses provided predicate (second parameter of binary function) to verify its input\n     * parameter. If predicate returns {@code true} it applies a provided integer function\n     * (first parameter of binary function) and returns a result value, otherwise it returns an element itself.\n     *\n     * @return a binary function that receiver predicate and function and compose them to create a new function\n     */\n    public static BiFunction<IntUnaryOperator, IntPredicate, IntUnaryOperator> functionToConditionalFunction() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link BiFunction} which first parameter is a {@link Map} where key is a function name, and value is some\n     * {@link IntUnaryOperator}, and second parameter is a {@link String} which is a function name. If the map contains a\n     * function by a given name then it is returned by high order function otherwise an identity() is returned.\n     *\n     * @return a high-order function that fetches a function from a function map by a given name or returns identity()\n     */\n    public static BiFunction<Map<String, IntUnaryOperator>, String, IntUnaryOperator> functionLoader() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a comparator of type T that is comparing values extracted using the provided mapper function.\n     * <p>\n     * E.g. imagine you need to compare accounts by their balance values.\n     * <pre>{@code\n     * Comparator<Account> balanceComparator = comparing(Account::getBalance);\n     * }</pre>\n     * <p>\n     * PLEASE NOTE, that @{@link Comparator} is a functional interface, and you should manually write a lambda expression\n     * to implement it.\n     *\n     * @param mapper a mapper function that allows to map an object to a comparable value\n     * @return a comparator instance\n     */\n    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> mapper) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a comparator of type T that uses a provided comparator to compare objects, and only if they are equal\n     * it's comparing values extracted using the provided mapper function.\n     * <p>\n     * E.g. suppose you want to compare accounts by balance, but in case two people have the same balance you want to\n     * compare their first names:\n     * <pre>{@code\n     * Comparator<Account> accountComparator = thenComparing(balanceComparator, Account::getFirstName);\n     * }</pre>\n     * <p>\n     *\n     * @param comparator an initial comparator\n     * @param mapper     a mapper function that is used to extract values when initial comparator returns zero\n     * @return a comparator instance\n     */\n    public static <T, U extends Comparable<? super U>> Comparator<T> thenComparing(\n            Comparator<? super T> comparator, Function<? super T, ? extends U> mapper) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns {@link Supplier} of {@link Supplier} of {@link Supplier} of {@link String} \"WELL DONE!\".\n     *\n     * @return a supplier instance\n     */\n    public static Supplier<Supplier<Supplier<String>>> trickyWellDoneSupplier() {\n        throw new ExerciseNotCompletedException();\n    }\n}\n\n"
  },
  {
    "path": "5-0-functional-programming/5-1-1-crazy-lambdas/src/test/java/com/bobocode/fp/CrazyLambdasTest.java",
    "content": "package com.bobocode.fp;\n\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\nimport java.math.BigDecimal;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.function.*;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * A test class for {@link CrazyLambdas}.\n *\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class CrazyLambdasTest {\n\n    @Test\n    @Order(1)\n    void helloSupplier() {\n        Supplier<String> helloSupplier = CrazyLambdas.helloSupplier();\n\n        assertEquals(\"Hello\", helloSupplier.get());\n    }\n\n\n    @Test\n    @Order(2)\n    void isEmptyPredicate() {\n        Predicate<String> isEmptyPredicate = CrazyLambdas.isEmptyPredicate();\n\n        boolean nonEmptyStringResult = isEmptyPredicate.test(\"fasdfa\");\n        boolean emptyStringResult = isEmptyPredicate.test(\"\");\n\n        assertFalse(nonEmptyStringResult);\n        assertTrue(emptyStringResult);\n    }\n\n    @Test\n    @Order(3)\n    void stringMultiplier() {\n        BiFunction<String, Integer, String> stringMultiplier = CrazyLambdas.stringMultiplier();\n\n        String threeTimesHi = stringMultiplier.apply(\"Hi\", 3);\n        String twoTimesHello = stringMultiplier.apply(\"Hello\", 2);\n\n        assertEquals(\"HiHiHi\", threeTimesHi);\n        assertEquals(\"HelloHello\", twoTimesHello);\n    }\n\n    @Test\n    @Order(4)\n    void toDollarStringFunction() {\n        Function<BigDecimal, String> toDollarStringFunction = CrazyLambdas.toDollarStringFunction();\n        String tenDollarStr = toDollarStringFunction.apply(BigDecimal.TEN.setScale(2));\n\n        assertEquals(\"$10.00\", tenDollarStr);\n    }\n\n    @Test\n    @Order(5)\n    void lengthInRangePredicate() {\n        Predicate<String> lengthInRangePredicate = CrazyLambdas.lengthInRangePredicate(4, 10);\n\n        boolean twoLetterStringResult = lengthInRangePredicate.test(\"Hi\");\n        boolean fourLetterStringResult = lengthInRangePredicate.test(\"Hola\");\n        boolean fiveLetterStringResult = lengthInRangePredicate.test(\"Amigo\");\n        boolean eightLetterStringResult = lengthInRangePredicate.test(\"Lalaland\");\n        boolean thirteenLetterStringResult = lengthInRangePredicate.test(\"Lambda rocks!\");\n\n        assertFalse(twoLetterStringResult);\n        assertTrue(fourLetterStringResult);\n        assertTrue(fiveLetterStringResult);\n        assertTrue(eightLetterStringResult);\n        assertFalse(thirteenLetterStringResult);\n    }\n\n    @Test\n    @Order(6)\n    void randomIntSupplier() {\n        IntSupplier randomIntSupplier = CrazyLambdas.randomIntSupplier();\n\n        int firstValue = randomIntSupplier.getAsInt();\n        int secondValue = randomIntSupplier.getAsInt();\n\n        assertNotEquals(firstValue, secondValue);\n    }\n\n    @Test\n    @Order(7)\n    void boundedRandomIntSupplier() {\n        IntUnaryOperator boundedRandomIntSupplier = CrazyLambdas.boundedRandomIntSupplier();\n\n        int randomIntLessThan10 = boundedRandomIntSupplier.applyAsInt(10);\n        int randomIntLessThan100 = boundedRandomIntSupplier.applyAsInt(100);\n        int randomIntLessThan1000 = boundedRandomIntSupplier.applyAsInt(1000);\n        int randomIntLessThan10000 = boundedRandomIntSupplier.applyAsInt(1000);\n\n        assertTrue(randomIntLessThan10 < 10);\n        assertTrue(randomIntLessThan100 < 100);\n        assertTrue(randomIntLessThan1000 < 1000);\n        assertTrue(randomIntLessThan10000 < 10000);\n    }\n\n    @Test\n    @Order(8)\n    void intSquareOperation() {\n        IntUnaryOperator squareOperation = CrazyLambdas.intSquareOperation();\n\n        int squareOfFour = squareOperation.applyAsInt(4);\n        int squareOfZero = squareOperation.applyAsInt(0);\n\n        assertEquals(16, squareOfFour);\n        assertEquals(0, squareOfZero);\n    }\n\n    @Test\n    @Order(9)\n    void longSumOperation() {\n        LongBinaryOperator sumOperation = CrazyLambdas.longSumOperation();\n\n\n        long sumOfSevenAndEight = sumOperation.applyAsLong(7, 8);\n        long sumOfTenAndZero = sumOperation.applyAsLong(10, 0);\n        long sumOfFiveAndMinusTen = sumOperation.applyAsLong(5, -10);\n\n        assertEquals(15, sumOfSevenAndEight);\n        assertEquals(10, sumOfTenAndZero);\n        assertEquals(-5, sumOfFiveAndMinusTen);\n    }\n\n    @Test\n    @Order(10)\n    void stringToIntConverter() {\n        ToIntFunction<String> stringToIntConverter = CrazyLambdas.stringToIntConverter();\n\n        int num = stringToIntConverter.applyAsInt(\"234\");\n        int negativeNum = stringToIntConverter.applyAsInt(\"-122\");\n\n        assertEquals(234, num);\n        assertEquals(-122, negativeNum);\n    }\n\n    @Test\n    @Order(11)\n    void nMultiplyFunctionSupplier() {\n        Supplier<IntUnaryOperator> fiveMultiplyFunctionSupplier = CrazyLambdas.nMultiplyFunctionSupplier(5);\n\n        IntUnaryOperator multiplyByFiveOperation = fiveMultiplyFunctionSupplier.get();\n        int result = multiplyByFiveOperation.applyAsInt(11); // 11 * 5 = 55\n\n        assertEquals(55, result);\n    }\n\n    @Test\n    @Order(12)\n    void composeWithTrimFunction() {\n        UnaryOperator<Function<String, String>> composeWithTrimFunction = CrazyLambdas.composeWithTrimFunction();\n        Function<String, String> toLowerWithTrim = composeWithTrimFunction.apply(String::toLowerCase);\n        Function<String, String> threeTimesRepeatWithTrim = composeWithTrimFunction.apply(s -> s.repeat(3));\n\n        String hey = toLowerWithTrim.apply(\"  Hey \");\n        String threeTimesHi = threeTimesRepeatWithTrim.apply(\"  Hi  \");\n\n        assertEquals(\"hey\", hey);\n        assertEquals(\"HiHiHi\", threeTimesHi);\n    }\n\n    @Test\n    @Order(13)\n    void runningThreadSupplier() throws InterruptedException {\n        Queue<Integer> concurrentLinkedQueue = new ConcurrentLinkedQueue<>();\n        Supplier<Thread> runningThreadSupplier = CrazyLambdas.runningThreadSupplier(() -> concurrentLinkedQueue.add(25));\n\n        // supplier does not create and start a thread before you call get()\n        assertEquals(0, concurrentLinkedQueue.size());\n\n        Thread runningThread = runningThreadSupplier.get(); // new thread has been started\n        runningThread.join();\n\n        assertEquals(1, concurrentLinkedQueue.size());\n        assertEquals(25, concurrentLinkedQueue.element().intValue());\n    }\n\n    @Test\n    @Order(14)\n    void newThreadRunnableConsumer() throws InterruptedException {\n        Consumer<Runnable> newThreadRunnableConsumer = CrazyLambdas.newThreadRunnableConsumer();\n\n        Queue<Integer> concurrentLinkedQueue = new ConcurrentLinkedQueue<>();\n        newThreadRunnableConsumer.accept(() -> concurrentLinkedQueue.add(50));\n\n        Thread.sleep(500); // don't do that in real code\n\n        assertEquals(1, concurrentLinkedQueue.size());\n        assertEquals(50, concurrentLinkedQueue.element().intValue());\n    }\n\n    @Test\n    @Order(15)\n    void runnableToThreadSupplierFunction() throws InterruptedException {\n        Function<Runnable, Supplier<Thread>> runnableSupplierFunction = CrazyLambdas.runnableToThreadSupplierFunction();\n        Queue<Integer> concurrentLinkedQueue = new ConcurrentLinkedQueue<>();\n\n        Supplier<Thread> threadSupplier = runnableSupplierFunction.apply(() -> concurrentLinkedQueue.add(200));\n\n        assertEquals(0, concurrentLinkedQueue.size()); // supplier does not create and start a thread before you call get()\n\n        Thread thread = threadSupplier.get();// new thread has been started\n        thread.join();\n\n        assertEquals(1, concurrentLinkedQueue.size());\n        assertEquals(200, concurrentLinkedQueue.element().intValue());\n    }\n\n    @Test\n    @Order(16)\n    void functionToConditionalFunction() {\n        BiFunction<IntUnaryOperator, IntPredicate, IntUnaryOperator> intFunctionToConditionalIntFunction\n                = CrazyLambdas.functionToConditionalFunction();\n\n        IntUnaryOperator abs = intFunctionToConditionalIntFunction.apply(a -> -a, a -> a < 0);\n\n        assertEquals(5, abs.applyAsInt(-5));\n        assertEquals(0, abs.applyAsInt(0));\n        assertEquals(5, abs.applyAsInt(5));\n    }\n\n    @Test\n    @Order(17)\n    void functionLoader() {\n        BiFunction<Map<String, IntUnaryOperator>, String, IntUnaryOperator> functionLoader = CrazyLambdas.functionLoader();\n        Map<String, IntUnaryOperator> functionMap = new HashMap<>();\n        functionMap.put(\"increment\", x -> x + 1);\n        functionMap.put(\"square\", x -> x * x);\n\n        IntUnaryOperator incrementFunction = functionLoader.apply(functionMap, \"increment\");\n        IntUnaryOperator squareFunction = functionLoader.apply(functionMap, \"square\");\n        IntUnaryOperator identityFunction = functionLoader.apply(functionMap, \"none\");\n\n        assertEquals(5, incrementFunction.applyAsInt(4));\n        assertEquals(9, squareFunction.applyAsInt(3));\n        assertEquals(10, identityFunction.applyAsInt(10));\n    }\n\n    @Test\n    @Order(18)\n    void comparing() {\n        var strLengthComparator = CrazyLambdas.comparing(String::length);\n        var stringList = new ArrayList<>(List.of(\"Me\", \"I\", \"All of us\", \"They\", \"She\"));\n\n        stringList.sort(strLengthComparator);\n\n        assertThat(stringList).isEqualTo(List.of(\"I\", \"Me\", \"She\", \"They\", \"All of us\"));\n    }\n\n    @Test\n    @Order(19)\n    void thenComparing() {\n        var strLengthComparator = Comparator.comparing(String::length);\n        var lengthThenNaturalComparator = CrazyLambdas.thenComparing(strLengthComparator, s -> s);\n        var stringList = new ArrayList<>(List.of(\"Me\", \"I\", \"All of us\", \"They\", \"She\", \"He\"));\n\n        stringList.sort(lengthThenNaturalComparator);\n\n        assertThat(stringList).isEqualTo(List.of(\"I\", \"He\", \"Me\", \"She\", \"They\", \"All of us\"));\n    }\n\n    @Test\n    @Order(20)\n    void trickyWellDoneSupplier() {\n        Supplier<Supplier<Supplier<String>>> wellDoneSupplier = CrazyLambdas.trickyWellDoneSupplier();\n\n        String wellDoneStr = wellDoneSupplier.get().get().get();\n\n        assertEquals(\"WELL DONE!\", wellDoneStr);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Crazy Streams\nTake your Stream API skills to the next level 💪\n\n### Task\n`CrazyStreams` provides an API with various statistic methods for a list of accounts. \nYour job is to implement each method of that class using **Stream API**. \nTo verify your implementation, run `CrazyStreamsTest.java`\n \n### Related materials ℹ️\n* [Modern Java in Action (chapters 4 - 6)](https://www.amazon.com/dp/1617293563/ref=cm_sw_r_tw_dp_XN3TPAASB945V8XQPW5F) 📕\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/o1H6kMlCQ74/0.jpg)](https://www.youtube.com/watch?v=o1H6kMlCQ74)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>5-0-functional-programming</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>5-2-1-crazy-streams</artifactId>\n\n\n</project>"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/src/main/java/com/bobocode/fp/CrazyStreams.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.model.Account;\nimport com.bobocode.util.ExerciseNotCompletedException;\nimport lombok.AllArgsConstructor;\n\nimport java.math.BigDecimal;\nimport java.time.Month;\nimport java.util.*;\n\n/**\n * {@link CrazyStreams} is an exercise class. Each method represent some operation with a collection of accounts that\n * should be implemented using Stream API. Every method that is not implemented yet throws\n * {@link ExerciseNotCompletedException}.\n * <p>\n * TODO: remove exception throwing and implement each method using Stream API\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @author Taras Boychuk\n */\n@AllArgsConstructor\npublic class CrazyStreams {\n    private Collection<Account> accounts;\n\n    /**\n     * Returns {@link Optional} that contains an {@link Account} with the max value of balance\n     *\n     * @return account with max balance wrapped with optional\n     */\n    public Optional<Account> findRichestPerson() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link List} of {@link Account} that have a birthday month equal to provided.\n     *\n     * @param birthdayMonth a month of birth\n     * @return a list of accounts\n     */\n    public List<Account> findAccountsByBirthdayMonth(Month birthdayMonth) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a map that separates all accounts into two lists - male and female. Map has two keys {@code true} indicates\n     * male list, and {@code false} indicates female list.\n     *\n     * @return a map where key is true or false, and value is list of male, and female accounts\n     */\n    public Map<Boolean, List<Account>> partitionMaleAccounts() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Map} that stores accounts grouped by its email domain. A map key is {@link String} which is an\n     * email domain like \"gmail.com\". And the value is a {@link List} of {@link Account} objects with a specific email domain.\n     *\n     * @return a map where key is an email domain and value is a list of all account with such email\n     */\n    public Map<String, List<Account>> groupAccountsByEmailDomain() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a number of letters in all first and last names.\n     *\n     * @return total number of letters of first and last names of all accounts\n     */\n    public int getNumOfLettersInFirstAndLastNames() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a total balance of all accounts.\n     *\n     * @return total balance of all accounts\n     */\n    public BigDecimal calculateTotalBalance() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link List} of {@link Account} objects sorted by first and last names.\n     *\n     * @return list of accounts sorted by first and last names\n     */\n    public List<Account> sortByFirstAndLastNames() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Checks if there is at least one account with provided email domain.\n     *\n     * @param emailDomain\n     * @return true if there is an account that has an email with provided domain\n     */\n    public boolean containsAccountWithEmailDomain(String emailDomain) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns account balance by its email. Throws {@link EntityNotFoundException} with message\n     * \"Cannot find Account by email={email}\" if account is not found.\n     *\n     * @param email account email\n     * @return account balance\n     */\n    public BigDecimal getBalanceByEmail(String email) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Collects all existing accounts into a {@link Map} where a key is account id, and the value is {@link Account} instance\n     *\n     * @return map of accounts by its ids\n     */\n    public Map<Long, Account> collectAccountsById() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Filters accounts by the year when an account was created. Collects account balances by its emails into a {@link Map}.\n     * The key is {@link Account#email} and the value is {@link Account#balance}\n     *\n     * @param year the year of account creation\n     * @return map of account by its ids the were created in a particular year\n     */\n    public Map<String, BigDecimal> collectBalancesByEmailForAccountsCreatedOn(int year) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Map} where key is {@link Account#lastName} and values is a {@link Set} that contains first names\n     * of all accounts with a specific last name.\n     *\n     * @return a map where key is a last name and value is a set of first names\n     */\n    public Map<String, Set<String>> groupFirstNamesByLastNames() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Map} where key is a birthday month, and value is a {@link String} that stores comma and space\n     * -separated first names (e.g. \"Polly, Dylan, Clark\"), of all accounts that have the same birthday month.\n     *\n     * @return a map where a key is a birthday month and value is comma-separated first names\n     */\n    public Map<Month, String> groupCommaSeparatedFirstNamesByBirthdayMonth() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Map} where key is a {@link Month} of {@link Account#creationDate}, and value is total balance\n     * of all accounts that have the same value creation month.\n     *\n     * @return a map where key is a creation month and value is total balance of all accounts created in that month\n     */\n    public Map<Month, BigDecimal> groupTotalBalanceByCreationMonth() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Map} where key is a letter {@link Character}, and value is a number of its occurrences in\n     * {@link Account#firstName}.\n     *\n     * @return a map where key is a letter and value is its count in all first names\n     */\n    public Map<Character, Long> getCharacterFrequencyInFirstNames() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns a {@link Map} where key is a letter {@link Character}, and value is a number of its occurrences ignoring\n     * case, in all {@link Account#firstName} and {@link Account#lastName} that are equal or longer than nameLengthBound.\n     * Inside the map, all letters should be stored in lower case.\n     *\n     * @return a map where key is a letter and value is its count ignoring case in all first and last names\n     */\n    public Map<Character, Long> getCharacterFrequencyIgnoreCaseInFirstAndLastNames(int nameLengthBound) {\n        throw new ExerciseNotCompletedException();\n    }\n\n}\n\n"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/src/main/java/com/bobocode/fp/exception/EntityNotFoundException.java",
    "content": "package com.bobocode.fp.exception;\n\npublic class EntityNotFoundException extends RuntimeException {\n    public EntityNotFoundException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/src/test/java/com/bobocode/fp/CrazyStreamsTest.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.fp.exception.EntityNotFoundException;\nimport com.bobocode.model.Account;\nimport com.bobocode.model.Sex;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.*;\nimport java.util.stream.Stream;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * A test class for {@link CrazyStreams}.\n * <p>\n * Please note that the helper methods of this test class do not use Stream API intentionally. You should try to find\n * a stream-based solutions for {@link CrazyStreams} by yourself.\n *\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\npublic class CrazyStreamsTest {\n\n    private CrazyStreams streams;\n    private static List<Account> accounts = Arrays.asList(\n            new Account(1L, \"Justin\", \"Butler\", \"justin.butler@gmail.com\",\n                    LocalDate.parse(\"2003-04-17\"), Sex.MALE, LocalDate.parse(\"2016-06-13\"), BigDecimal.valueOf(172966)),\n            new Account(2L, \"Olivia\", \"Cardenas\", \"cardenas@mail.com\",\n                    LocalDate.parse(\"1930-01-19\"), Sex.FEMALE, LocalDate.parse(\"2014-06-21\"), BigDecimal.valueOf(38029)),\n            new Account(3L, \"Nolan\", \"Donovan\", \"nolandonovan@gmail.com\",\n                    LocalDate.parse(\"1925-04-19\"), Sex.MALE, LocalDate.parse(\"2011-03-10\"), BigDecimal.valueOf(13889)),\n            new Account(4L, \"Lucas\", \"Lynn\", \"lucas.lynn@yahoo.com\",\n                    LocalDate.parse(\"1987-05-25\"), Sex.MALE, LocalDate.parse(\"2009-03-05\"), BigDecimal.valueOf(16980))\n    );\n\n    @BeforeEach\n    void setUp() {\n        streams = new CrazyStreams(accounts);\n    }\n\n    @Test\n    @Order(1)\n    void findRichestPerson() {\n        Optional<Account> expectedPerson = Optional.of(accounts.get(0));\n        Optional<Account> actualRichestPerson = streams.findRichestPerson();\n\n        assertEquals(expectedPerson, actualRichestPerson);\n    }\n\n    @Test\n    @Order(2)\n    void findAccountsByBirthdayMonth() {\n        List<Account> expectedList = getExpectedList();\n        List<Account> aprilAccounts = streams.findAccountsByBirthdayMonth(Month.APRIL);\n\n        assertEquals(expectedList, aprilAccounts);\n    }\n\n    @Test\n    @Order(3)\n    void separateMaleAccounts() {\n        Map<Boolean, List<Account>> expectedAccountMap = getExpectedMaleMap();\n        Map<Boolean, List<Account>> maleToAccountsMap = streams.partitionMaleAccounts();\n\n        assertEquals(expectedAccountMap, maleToAccountsMap);\n    }\n\n    private Map<Boolean, List<Account>> getExpectedMaleMap() {\n        Map<Boolean, List<Account>> expectedMap = new HashMap<>(2);\n        expectedMap.put(Boolean.TRUE, Arrays.asList(accounts.get(0), accounts.get(2), accounts.get(3)));\n        expectedMap.put(Boolean.FALSE, Arrays.asList(accounts.get(1)));\n        return expectedMap;\n    }\n\n    private List<Account> getExpectedList() {\n        return Arrays.asList(accounts.get(0), accounts.get(2));\n    }\n\n    @Test\n    @Order(4)\n    void groupAccountsByEmailDomain() {\n        Map<String, List<Account>> expectedEmailMap = getExpectedEmailMap();\n        Map<String, List<Account>> emailDomainToAccountsMap = streams.groupAccountsByEmailDomain();\n\n        assertEquals(expectedEmailMap, emailDomainToAccountsMap);\n    }\n\n    private Map<String, List<Account>> getExpectedEmailMap() {\n        Map<String, List<Account>> expectedEmailMap = new HashMap<>();\n        expectedEmailMap.put(\"gmail.com\", Arrays.asList(accounts.get(0), accounts.get(2)));\n        expectedEmailMap.put(\"mail.com\", Arrays.asList(accounts.get(1)));\n        expectedEmailMap.put(\"yahoo.com\", Arrays.asList(accounts.get(3)));\n\n        return expectedEmailMap;\n    }\n\n    @Test\n    @Order(5)\n    void getNumOfLettersInFirstAndLastNames() {\n        int numOfLettersInFirstAndLastNames = streams.getNumOfLettersInFirstAndLastNames();\n\n        assertEquals(47, numOfLettersInFirstAndLastNames);\n    }\n\n    @Test\n    @Order(6)\n    void calculateTotalBalance() {\n        BigDecimal totalBalance = streams.calculateTotalBalance();\n\n        assertEquals(BigDecimal.valueOf(241864), totalBalance);\n    }\n\n\n    @Test\n    @Order(7)\n    void sortByFirstAndLastNames() {\n        List<Account> sortedList = streams.sortByFirstAndLastNames();\n\n        assertEquals(1L, sortedList.get(0).getId().longValue());\n        assertEquals(4L, sortedList.get(1).getId().longValue());\n        assertEquals(3L, sortedList.get(2).getId().longValue());\n        assertEquals(2L, sortedList.get(3).getId().longValue());\n\n    }\n\n    @Test\n    @Order(8)\n    void containsAccountWithEmailDomain() {\n        assertTrue(streams.containsAccountWithEmailDomain(\"gmail.com\"));\n        assertTrue(streams.containsAccountWithEmailDomain(\"yahoo.com\"));\n        assertFalse(streams.containsAccountWithEmailDomain(\"ukr.net\"));\n    }\n\n    @Test\n    @Order(9)\n    void getBalanceByEmail() {\n        Account account = accounts.get(1);\n        BigDecimal balance = streams.getBalanceByEmail(account.getEmail());\n\n        assertEquals(account.getBalance(), balance);\n    }\n\n    @Test\n    @Order(10)\n    void getBalanceByEmailThrowsException() {\n        String fakeEmail = \"fake@mail.com\";\n        try {\n            streams.getBalanceByEmail(fakeEmail);\n            fail(\"Should throw exception\");\n        } catch (Exception e) {\n            assertTrue(e instanceof EntityNotFoundException);\n            assertEquals(String.format(\"Cannot find Account by email=%s\", fakeEmail), e.getMessage());\n        }\n    }\n\n    @Test\n    @Order(11)\n    void collectAccountsById() {\n        Map<Long, Account> idToAccountMap = streams.collectAccountsById();\n\n        assertEquals(accounts.get(0), idToAccountMap.get(1L));\n        assertEquals(accounts.get(1), idToAccountMap.get(2L));\n        assertEquals(accounts.get(2), idToAccountMap.get(3L));\n        assertEquals(accounts.get(3), idToAccountMap.get(4L));\n    }\n\n    @Test\n    @Order(12)\n    void collectBalancesByEmailForAccountsCreatedOn() {\n        Account account = accounts.get(3);\n\n        Map<String, BigDecimal> emailToBalanceMap = streams.collectBalancesByEmailForAccountsCreatedOn(account.getCreationDate().getYear());\n\n        assertEquals(Map.of(account.getEmail(), account.getBalance()), emailToBalanceMap);\n    }\n\n    @Test\n    @Order(13)\n    void groupFirstNamesByLastNames() {\n        Map<String, Set<String>> lastToFirstNamesMap = streams.groupFirstNamesByLastNames();\n\n        assertEquals(4, lastToFirstNamesMap.size());\n        assertEquals(Set.of(\"Justin\"), lastToFirstNamesMap.get(\"Butler\"));\n        assertEquals(Set.of(\"Olivia\"), lastToFirstNamesMap.get(\"Cardenas\"));\n        assertEquals(Set.of(\"Nolan\"), lastToFirstNamesMap.get(\"Donovan\"));\n        assertEquals(Set.of(\"Lucas\"), lastToFirstNamesMap.get(\"Lynn\"));\n    }\n\n    @Test\n    @Order(14)\n    void groupCommaSeparatedFirstNamesByBirthdayMonth() {\n        Map<Month, String> birthdayMonthToFirstNamesMap = streams.groupCommaSeparatedFirstNamesByBirthdayMonth();\n\n        assertEquals(3, birthdayMonthToFirstNamesMap.size());\n        assertEquals(\"Olivia\", birthdayMonthToFirstNamesMap.get(Month.JANUARY));\n        assertEquals(\"Justin, Nolan\", birthdayMonthToFirstNamesMap.get(Month.APRIL));\n        assertEquals(\"Lucas\", birthdayMonthToFirstNamesMap.get(Month.MAY));\n    }\n\n    @Test\n    @Order(15)\n    void groupTotalBalanceByCreationMonth() {\n        Map<Month, BigDecimal> totalBalanceByAccountCreationMonth = streams.groupTotalBalanceByCreationMonth();\n\n        assertEquals(2, totalBalanceByAccountCreationMonth.size());\n        assertEquals(BigDecimal.valueOf(210995), totalBalanceByAccountCreationMonth.get(Month.JUNE));\n        assertEquals(BigDecimal.valueOf(30869), totalBalanceByAccountCreationMonth.get(Month.MARCH));\n    }\n\n    @Test\n    @Order(16)\n    void getCharacterFrequencyInFirstNames() {\n        Map<Character, Long> characterFrequencyInFirstAndLastNames = streams.getCharacterFrequencyInFirstNames();\n\n        assertEquals(3, characterFrequencyInFirstAndLastNames.get('a').longValue());\n        assertEquals(1, characterFrequencyInFirstAndLastNames.get('c').longValue());\n        assertEquals(3, characterFrequencyInFirstAndLastNames.get('i').longValue());\n        assertEquals(1, characterFrequencyInFirstAndLastNames.get('J').longValue());\n        assertEquals(1, characterFrequencyInFirstAndLastNames.get('L').longValue());\n        assertEquals(2, characterFrequencyInFirstAndLastNames.get('l').longValue());\n        assertEquals(2, characterFrequencyInFirstAndLastNames.get('u').longValue());\n    }\n\n    @MethodSource(\"getCharacterFrequencyIgnoreCaseInFirstAndLastNamesArgs\")\n    @ParameterizedTest\n    @Order(17)\n    void getCharacterFrequencyIgnoreCaseInFirstAndLastNames(int nameLengthBound, Map<Character, Long> resultMap) {\n        var characterFrequencyInFirstAndLastNames = streams.getCharacterFrequencyIgnoreCaseInFirstAndLastNames(nameLengthBound);\n\n        assertThat(characterFrequencyInFirstAndLastNames).isEqualTo(resultMap);\n    }\n\n    private static Stream<Arguments> getCharacterFrequencyIgnoreCaseInFirstAndLastNamesArgs() {\n        return Stream.of(\n                Arguments.arguments(2, buildMap(accounts, 2)),\n                Arguments.arguments(5, buildMap(accounts, 5)),\n                Arguments.arguments(7, buildMap(accounts, 7))\n        );\n    }\n\n    private static Map<Character, Long> buildMap(List<Account> accounts, int nameLengthBound) {\n        var resultMap = new HashMap<Character, Long>();\n        for (var a : accounts) {\n            processName(resultMap, a.getFirstName(), nameLengthBound);\n            processName(resultMap, a.getLastName(), nameLengthBound);\n        }\n        return resultMap;\n    }\n\n    private static void processName(Map<Character, Long> resultMap, String name, int nameLengthBound) {\n        if (name.length() >= nameLengthBound) {\n            var chars = name.toLowerCase().toCharArray();\n            for (Character c : chars) {\n                if (resultMap.putIfAbsent(c, 1L) != null) {\n                    resultMap.compute(c, (k, counter) -> counter + 1L);\n                }\n            }\n        }\n    }\n}\n\n\n"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Crazy Optionals\nImprove your Optional API skills 💪\n### Task\n`CrazyOptionals` class consists of static methods that require implementation. \nYour job is to implement the *todo* section of that class using **Optional API**. \nTo verify your implementation, run `CrazyOptionalsTest.java`\n\n### Related materials :information_source:\n  * [Optional у Java 9 <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/yt_icon_rgb.png\" height=13/>](https://youtu.be/YSZVyOHLbjk)\n  * [Stream API. Optional. Курс Enterprise Java <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/yt_icon_rgb.png\" height=13/>](https://youtu.be/OD0hIs1cGmY)\n\n### Exercise overview 🇺🇦\n[![Watch the video](https://img.youtube.com/vi/yyhD0QZGMRs/0.jpg)](https://www.youtube.com/watch?v=yyhD0QZGMRs)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>5-0-functional-programming</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>5-3-1-crazy-optionals</artifactId>\n\n\n</project>"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/CrazyOptionals.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.fp.exception.AccountNotFoundException;\nimport com.bobocode.fp.function.AccountProvider;\nimport com.bobocode.fp.function.AccountService;\nimport com.bobocode.fp.function.CreditAccountProvider;\nimport com.bobocode.model.Account;\nimport com.bobocode.model.CreditAccount;\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.OptionalDouble;\n\n/**\n * {@link CrazyOptionals} is an exercise class. Each method represents some operation with a {@link Account} and\n * should be implemented using Optional API. Every method that is not implemented yet throws\n * {@link ExerciseNotCompletedException}.\n * <p>\n * TODO: remove exception and implement each method of this class using Optional API\n * <p><p>\n * <strong>TODO: to get the most out of your learning, <a href=\"https://www.bobocode.com\">visit our website</a></strong>\n * <p>\n *\n * @author Taras Boychuk\n */\npublic class CrazyOptionals {\n\n    /**\n     * Creates an instance of {@link Optional<String>} using a text parameter\n     *\n     * @param text\n     * @return optional object that holds text\n     */\n    public static Optional<String> optionalOfString(@Nullable String text) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Adds a provided amount of money to the balance of an optional account.\n     *\n     * @param accountProvider\n     * @param amount          money to deposit\n     */\n    public static void deposit(AccountProvider accountProvider, BigDecimal amount) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Creates an instance of {@link Optional<Account>} using an account parameter\n     *\n     * @param account\n     * @return optional object that holds account\n     */\n    public static Optional<Account> optionalOfAccount(@Nonnull Account account) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns the {@link Account} got from {@link AccountProvider}. If account provider does not provide an account,\n     * returns a defaultAccount\n     *\n     * @param accountProvider\n     * @param defaultAccount\n     * @return account from provider or defaultAccount\n     */\n    public static Account getAccount(AccountProvider accountProvider, Account defaultAccount) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Passes account to {@link AccountService#processAccount(Account)} when account is provided.\n     * Otherwise calls {@link AccountService#processWithNoAccount()}\n     *\n     * @param accountProvider\n     * @param accountService\n     */\n    public static void processAccount(AccountProvider accountProvider, AccountService accountService) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns account provided by {@link AccountProvider}. If no account is provided it generates one using {@link Accounts}\n     * Please note that additional account should not be generated if {@link AccountProvider} returned one.\n     *\n     * @param accountProvider\n     * @return provided or generated account\n     */\n    public static Account getOrGenerateAccount(AccountProvider accountProvider) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Retrieves a {@link BigDecimal} balance using null-safe mappings.\n     *\n     * @param accountProvider\n     * @return optional balance\n     */\n    public static Optional<BigDecimal> retrieveBalance(AccountProvider accountProvider) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns an {@link Account} provided by {@link AccountProvider}. If no account is provided,\n     * throws {@link AccountNotFoundException} with a message \"No Account provided!\"\n     *\n     * @param accountProvider\n     * @return provided account\n     */\n    public static Account getAccount(AccountProvider accountProvider) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Retrieves a {@link BigDecimal} credit balance using null-safe mappings.\n     *\n     * @param accountProvider\n     * @return optional credit balance\n     */\n    public static Optional<BigDecimal> retrieveCreditBalance(CreditAccountProvider accountProvider) {\n        throw new ExerciseNotCompletedException();\n    }\n\n\n    /**\n     * Retrieves an {@link Account} with gmail email using {@link AccountProvider}. If no account is provided or it gets\n     * not gmail then returns {@link Optional#empty()}\n     *\n     * @param accountProvider\n     * @return optional gmail account\n     */\n    public static Optional<Account> retrieveAccountGmail(AccountProvider accountProvider) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Retrieves account using {@link AccountProvider} and fallbackProvider. In case main provider does not provide an\n     * {@link Account} then account should ge retrieved from fallbackProvider. In case both provider returns no account\n     * then {@link java.util.NoSuchElementException} should be thrown.\n     *\n     * @param accountProvider\n     * @param fallbackProvider\n     * @return account got from either accountProvider or fallbackProvider\n     */\n    public static Account getAccountWithFallback(AccountProvider accountProvider, AccountProvider fallbackProvider) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns an {@link Accounts} with the highest balance. Throws {@link java.util.NoSuchElementException} if input\n     * list is empty\n     *\n     * @param accounts\n     * @return account with the highest balance\n     */\n    public static Account getAccountWithMaxBalance(List<Account> accounts) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Returns the lowest balance values or empty if accounts list is empty\n     *\n     * @param accounts\n     * @return the lowest balance values\n     */\n    public static OptionalDouble findMinBalanceValue(List<Account> accounts) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Finds an {@link Account} with max balance and processes it using {@link AccountService#processAccount(Account)}\n     *\n     * @param accounts\n     * @param accountService\n     */\n    public static void processAccountWithMaxBalance(List<Account> accounts, AccountService accountService) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    /**\n     * Calculates a sum of {@link CreditAccount#getCreditBalance()} of all accounts\n     *\n     * @param accounts\n     * @return total credit balance\n     */\n    public static double calculateTotalCreditBalance(List<CreditAccount> accounts) {\n        throw new ExerciseNotCompletedException();\n    }\n}\n\n"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/exception/AccountNotFoundException.java",
    "content": "package com.bobocode.fp.exception;\n\npublic class AccountNotFoundException extends RuntimeException {\n    public AccountNotFoundException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/AccountProvider.java",
    "content": "package com.bobocode.fp.function;\n\nimport com.bobocode.model.Account;\n\nimport java.util.Optional;\n\n@FunctionalInterface\npublic interface AccountProvider {\n    Optional<Account> getAccount();\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/AccountService.java",
    "content": "package com.bobocode.fp.function;\n\nimport com.bobocode.model.Account;\n\n@FunctionalInterface\npublic interface AccountService {\n    void processAccount(Account account);\n\n    default void processWithNoAccount(){\n        /* No operation */\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/CreditAccountProvider.java",
    "content": "package com.bobocode.fp.function;\n\nimport com.bobocode.model.CreditAccount;\n\nimport java.util.Optional;\n\n@FunctionalInterface\npublic interface CreditAccountProvider {\n    Optional<CreditAccount> getAccount();\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/test/java/com/bobocode/fp/CrazyOptionalsTest.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.fp.exception.AccountNotFoundException;\nimport com.bobocode.fp.function.AccountService;\nimport com.bobocode.model.Account;\nimport com.bobocode.model.CreditAccount;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.mockito.Mockito;\n\nimport java.math.BigDecimal;\nimport java.util.*;\n\nimport static java.util.Comparator.comparing;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\n/**\n * A test class for {@link CrazyOptionals}.\n *\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\nclass CrazyOptionalsTest {\n\n    @Test\n    @Order(1)\n    void optionalOfStringShouldAcceptNull() {\n        Optional<String> optionalString = CrazyOptionals.optionalOfString(null);\n\n        assertEquals(Optional.empty(), optionalString);\n    }\n\n    @Test\n    @Order(2)\n    void optionalOfStringShouldHoldSting() {\n        Optional<String> optionalString = CrazyOptionals.optionalOfString(\"Hello\");\n\n        assertEquals(\"Hello\", optionalString.get());\n    }\n\n    @Test\n    @Order(3)\n    void depositWhenAccountExistsShouldUpdateBalance() {\n        Account account = Accounts.generateAccount();\n        BigDecimal balanceBeforeUpdate = account.getBalance();\n        BigDecimal amountToAdd = BigDecimal.valueOf(1000);\n\n        CrazyOptionals.deposit(() -> Optional.of(account), amountToAdd);\n\n        assertEquals(balanceBeforeUpdate.add(amountToAdd), account.getBalance());\n    }\n\n    @Test\n    @Order(4)\n    void depositWhenOptionalIsEmptyShouldDoNothing() {\n        CrazyOptionals.deposit(Optional::empty, BigDecimal.ZERO);\n    }\n\n    @Test\n    @Order(5)\n    void optionalOfAccountShouldNotAcceptNull() {\n        assertThrows(NullPointerException.class, () -> CrazyOptionals.optionalOfAccount(null));\n    }\n\n    @Test\n    @Order(6)\n    void optionalOfAccountShouldHoldAccount() {\n        Account account = Accounts.generateAccount();\n        Optional<Account> optionalAccount = CrazyOptionals.optionalOfAccount(account);\n\n        assertEquals(account, optionalAccount.get());\n    }\n\n    @Test\n    @Order(7)\n    void getAccountShouldReturnAccountGotFromProvider() {\n        Account providedAccount = Accounts.generateAccount();\n        Account defaultAccount = Accounts.generateAccount();\n\n        Account receivedAccount = CrazyOptionals.getAccount(() -> Optional.of(providedAccount), defaultAccount);\n\n        assertEquals(providedAccount, receivedAccount);\n    }\n\n    @Test\n    @Order(8)\n    void getAccountWhenNoAccountIsProvidedShouldReturnDefaultAccount() {\n        Account defaultAccount = Accounts.generateAccount();\n\n        Account receivedAccount = CrazyOptionals.getAccount(Optional::empty, defaultAccount);\n\n        assertEquals(defaultAccount, receivedAccount);\n    }\n\n    @Test\n    @Order(9)\n    void processAccountWhenAccountIsProvidedShouldPassAccountToService() {\n        Account account = Accounts.generateAccount();\n        BigDecimal initialBalance = account.getBalance();\n        BigDecimal bonus = BigDecimal.valueOf(1000);\n\n        CrazyOptionals.processAccount(() -> Optional.of(account), a -> a.setBalance(account.getBalance().add(bonus)));\n\n        assertEquals(initialBalance.add(bonus), account.getBalance());\n    }\n\n    @Test\n    @Order(10)\n    void processAccountWhenNoAccountProvidedShouldProcessWithNoAccount() {\n        Optional<Account> optionalAccountSpy = spy(Optional.empty());\n        AccountService accountService = mock(AccountService.class);\n\n        CrazyOptionals.processAccount(() -> optionalAccountSpy, accountService);\n\n        verify(accountService, times(1)).processWithNoAccount();\n        verify(accountService, never()).processAccount(any());\n\n    }\n\n    @Test\n    @Order(11)\n    void processAccountShouldUseNullSafeDeclarativeIfStatement() {\n        Optional<Account> optionalAccountSpy = spy(Optional.empty());\n\n        CrazyOptionals.processAccount(() -> optionalAccountSpy, account -> {\n        });\n\n        verify(optionalAccountSpy, times(1)).ifPresentOrElse(any(), any());\n    }\n\n    @Test\n    @Order(12)\n    void getOrGenerateAccountShouldReturnAccountGotFromProvider() {\n        Account providedAccount = Accounts.generateAccount();\n\n        Account receivedAccount = CrazyOptionals.getOrGenerateAccount(() -> Optional.of(providedAccount));\n\n        assertEquals(providedAccount, receivedAccount);\n    }\n\n    @Test\n    @Order(13)\n    void getOrGenerateAccountWhenNoAccountIsProvidedShouldGenerateAccount() {\n        Account receivedAccount = CrazyOptionals.getOrGenerateAccount(Optional::empty);\n\n        assertNotNull(receivedAccount);\n    }\n\n    @Test\n    @Order(14)\n    void getOrGenerateAccountWhenNoAccountIsProvidedShouldUseLazyInitialization() {\n        Optional<Account> optionalAccountSpy = spy(Optional.empty());\n\n        CrazyOptionals.getOrGenerateAccount(() -> optionalAccountSpy);\n\n        verify(optionalAccountSpy, never()).orElse(any());\n        verify(optionalAccountSpy, never()).get();\n        verify(optionalAccountSpy, times(1)).orElseGet(any());\n    }\n\n    @Test\n    @Order(15)\n    void retrieveBalanceWhenAccountIsNotProvidedShouldReturnEmptyOptional() {\n        Optional<BigDecimal> balance = CrazyOptionals.retrieveBalance(Optional::empty);\n\n        assertFalse(balance.isPresent());\n    }\n\n    @Test\n    @Order(16)\n    void retrieveBalanceWhenBalanceIsNullShouldReturnEmptyOptional() {\n        Account account = Accounts.generateAccount();\n        account.setBalance(null);\n\n        Optional<BigDecimal> balance = CrazyOptionals.retrieveBalance(() -> Optional.of(account));\n\n        assertFalse(balance.isPresent());\n    }\n\n    @Test\n    @Order(17)\n    void retrieveBalanceShouldReturnOptionalBalance() {\n        Account account = Accounts.generateAccount();\n\n        Optional<BigDecimal> balance = CrazyOptionals.retrieveBalance(() -> Optional.of(account));\n\n        assertEquals(account.getBalance(), balance.get());\n    }\n\n    @Test\n    @Order(18)\n    void retrieveBalanceShouldUseNullSafeMapping() {\n        Account account = Accounts.generateAccount();\n        Optional<Account> optionalAccountSpy = spy(Optional.of(account));\n\n        CrazyOptionals.retrieveBalance(() -> optionalAccountSpy);\n\n        verify(optionalAccountSpy, never()).get();\n        verify(optionalAccountSpy, never()).orElse(any());\n        verify(optionalAccountSpy, never()).orElseGet(any());\n        verify(optionalAccountSpy, times(1)).map(any());\n    }\n\n    @Test\n    @Order(19)\n    void getAccountShouldReturnProvidedAccount() {\n        Account account = Accounts.generateAccount();\n\n        Account receivedAccount = CrazyOptionals.getAccount(() -> Optional.of(account));\n\n        assertEquals(account, receivedAccount);\n    }\n\n    @Test\n    @Order(20)\n    void getAccountWhenNoAccountIsProvidedShouldThrowAccountNotFoundException() {\n        AccountNotFoundException exception = assertThrows(AccountNotFoundException.class,\n                () -> CrazyOptionals.getAccount(Optional::empty));\n        assertEquals(\"No Account provided!\", exception.getMessage());\n    }\n\n    @Test\n    @Order(21)\n    void retrieveCreditBalanceWhenAccountIsNotProvidedShouldReturnEmptyOptional() {\n        Optional<BigDecimal> creditBalance = CrazyOptionals.retrieveCreditBalance(Optional::empty);\n\n        assertFalse(creditBalance.isPresent());\n    }\n\n    @Test\n    @Order(22)\n    void retrieveCreditBalanceWhenBalanceIsNullShouldReturnEmptyOptional() {\n        CreditAccount account = Accounts.generateCreditAccount();\n        account.setCreditBalance(null);\n\n        Optional<BigDecimal> creditBalance = CrazyOptionals.retrieveCreditBalance(() -> Optional.of(account));\n\n        assertFalse(creditBalance.isPresent());\n    }\n\n    @Test\n    @Order(23)\n    void retrieveCreditBalanceShouldReturnOptionalBalance() {\n        CreditAccount account = Accounts.generateCreditAccount();\n\n        Optional<BigDecimal> creditBalance = CrazyOptionals.retrieveCreditBalance(() -> Optional.of(account));\n\n        assertEquals(account.getCreditBalance(), creditBalance);\n    }\n\n    @Test\n    @Order(24)\n    void retrieveCreditBalanceShouldUseNullSafeMapping() {\n        CreditAccount creditAccount = Accounts.generateCreditAccount();\n        Optional<CreditAccount> optionalCreditAccountSpy = spy(Optional.of(creditAccount));\n\n        CrazyOptionals.retrieveCreditBalance(() -> optionalCreditAccountSpy);\n\n        verify(optionalCreditAccountSpy, never()).get();\n        verify(optionalCreditAccountSpy, never()).orElse(any());\n        verify(optionalCreditAccountSpy, never()).orElseGet(any());\n        verify(optionalCreditAccountSpy, times(1)).flatMap(any());\n    }\n\n    @Test\n    @Order(25)\n    void retrieveAccountGmailWhenNoAccountProvidedShouldReturnEmptyOptional() {\n        Optional<Account> optionalGmailAccount = CrazyOptionals.retrieveAccountGmail(Optional::empty);\n\n        assertEquals(Optional.empty(), optionalGmailAccount);\n    }\n\n    @Test\n    @Order(26)\n    void retrieveAccountGmailWhenAccountEmailIsNotGmailShouldReturnEmptyOptional() {\n        Account account = Accounts.generateCreditAccount();\n        account.setEmail(\"bobby@yahoo.com\");\n\n        Optional<Account> optionalGmailAccount = CrazyOptionals.retrieveAccountGmail(() -> Optional.of(account));\n\n        assertEquals(Optional.empty(), optionalGmailAccount);\n    }\n\n    @Test\n    @Order(27)\n    void retrieveAccountGmailWhenEmailIsGmailShouldReturnEmail() {\n        Account account = Accounts.generateCreditAccount();\n        account.setEmail(\"johnny@gmail.com\");\n\n        Optional<Account> optionalGmailAccount = CrazyOptionals.retrieveAccountGmail(() -> Optional.of(account));\n\n        assertEquals(account, optionalGmailAccount.get());\n    }\n\n    @Test\n    @Order(28)\n    void retrieveAccountGmailShouldUseNullSafeFiltering() {\n        Account account = Accounts.generateAccount();\n        account.setEmail(\"johnny@gmail.com\");\n        Optional<Account> optionalAccountSpy = spy(Optional.of(account));\n\n        CrazyOptionals.retrieveAccountGmail(() -> optionalAccountSpy);\n\n        verify(optionalAccountSpy, never()).get();\n        verify(optionalAccountSpy, never()).orElse(any());\n        verify(optionalAccountSpy, times(1)).filter(any());\n    }\n\n    @Test\n    @Order(29)\n    void getAccountWithFallbackShouldBeRetrievedFromMainProvider() {\n        Account account = Accounts.generateAccount();\n        Account fallbackAccount = Accounts.generateAccount();\n\n        Account retrievedAccount = CrazyOptionals.getAccountWithFallback(() -> Optional.of(account), () -> Optional.of(fallbackAccount));\n\n        assertEquals(account, retrievedAccount);\n    }\n\n    @Test\n    @Order(30)\n    void getAccountWithFallbackWhenNoAccountIsProvidedByMainProviderShouldUseFallback() {\n        Account fallbackAccount = Accounts.generateAccount();\n\n        Account retrievedAccount = CrazyOptionals.getAccountWithFallback(Optional::empty, () -> Optional.of(fallbackAccount));\n\n        assertEquals(fallbackAccount, retrievedAccount);\n    }\n\n    @Test\n    @Order(31)\n    void getAccountWithFallbackWhenNoAccountShouldThrowException() {\n        assertThrows(NoSuchElementException.class,\n                () -> CrazyOptionals.getAccountWithFallback(Optional::empty, Optional::empty));\n    }\n\n    @Test\n    @Order(32)\n    void getAccountWithFallbackShouldUseNullSafeFallbackStrategy() {\n        Optional<Account> optionalAccountSpy = spy(Optional.empty());\n\n        CrazyOptionals.getAccountWithFallback(() -> optionalAccountSpy, () -> Optional.of(Accounts.generateAccount()));\n\n        verify(optionalAccountSpy, times(1)).isPresent();\n        verify(optionalAccountSpy, never()).isEmpty();\n        verify(optionalAccountSpy, never()).get();\n        verify(optionalAccountSpy, never()).orElse(any());\n        verify(optionalAccountSpy, never()).orElseGet(any());\n        verify(optionalAccountSpy, times(1)).or(any());\n    }\n\n    @Test\n    @Order(33)\n    void getAccountWithMaxBalance() {\n        List<Account> accounts = Accounts.generateAccountList(5);\n        Account richestAccount = getMaxAccount(accounts, comparing(Account::getBalance));\n\n        Account receivedAccount = CrazyOptionals.getAccountWithMaxBalance(accounts);\n\n        assertEquals(richestAccount, receivedAccount);\n    }\n\n    @Test\n    @Order(34)\n    void getAccountWithMaxBalanceWhenListIsEmptyShouldThrowException() {\n        assertThrows(NoSuchElementException.class,\n                () -> CrazyOptionals.getAccountWithMaxBalance(Collections.emptyList()));\n    }\n\n    @Test\n    @Order(35)\n    void findMinBalanceValueShouldReturnCorrectDoubleValue() {\n        List<Account> accounts = Accounts.generateAccountList(5);\n        Account accountWithLowestBalance = getMaxAccount(accounts, comparing(Account::getBalance).reversed());\n        double expectedBalance = accountWithLowestBalance.getBalance().doubleValue();\n\n        OptionalDouble optionalMinBalance = CrazyOptionals.findMinBalanceValue(accounts);\n\n        assertEquals(expectedBalance, optionalMinBalance.getAsDouble(), 0.001);\n    }\n\n    @Test\n    @Order(36)\n    void findMinBalanceValueWhenListIsEmptyShouldReturnOptionalEmpty() {\n        OptionalDouble optionalMinBalance = CrazyOptionals.findMinBalanceValue(Collections.emptyList());\n\n        assertEquals(OptionalDouble.empty(), optionalMinBalance);\n    }\n\n    @Test\n    @Order(37)\n    void processAccountWithMaxBalance() {\n        List<Account> accounts = Accounts.generateAccountList(5);\n        Account richestAccount = getMaxAccount(accounts, comparing(Account::getBalance));\n        AccountService accountServiceSpy = Mockito.spy(AccountService.class);\n\n        CrazyOptionals.processAccountWithMaxBalance(accounts, accountServiceSpy);\n\n        verify(accountServiceSpy, times(1)).processAccount(richestAccount);\n    }\n\n    @Test\n    @Order(38)\n    void calculateTotalCreditBalanceShouldCalculateCorrectTotal() {\n        List<CreditAccount> accounts = Accounts.generateCreditAccountList(5);\n        double expectedTotal = calculateTotalCreditBalance(accounts);\n\n        double calculatedTotal = CrazyOptionals.calculateTotalCreditBalance(accounts);\n\n        assertEquals(expectedTotal, calculatedTotal, 0.001);\n    }\n\n    @Test\n    @Order(39)\n    void calculateTotalCreditBalanceWhenListIsEmptyShouldReturnZero() {\n        double calculatedTotal = CrazyOptionals.calculateTotalCreditBalance(Collections.emptyList());\n\n        assertEquals(0.0, calculatedTotal, 0.001);\n    }\n\n    private Account getMaxAccount(List<Account> accounts, Comparator<Account> comparator) {\n        return accounts.stream()\n                .max(comparator)\n                .orElseThrow();\n    }\n\n    private double calculateTotalCreditBalance(List<CreditAccount> accounts) {\n        return accounts.stream()\n                .map(CreditAccount::getCreditBalance)\n                .flatMap(Optional::stream)\n                .mapToDouble(BigDecimal::doubleValue)\n                .sum();\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/> Fun Prime Numbers \n\nImprove your functional programming skills implementing various operations based on an integer stream of Prime Numbers 💪\n\n### Pre-conditions ❗\nYou're supposed to be familiar with Lambdas, Stream API and Optional API\n\n### Objectives\n* create an **infinite stream** of integers with a **custom increment** ✅\n* **find matching elements** within a stream ✅\n* **limit** the number of elements within a stream ✅\n* **calculate a sum** of elements of an `IntStream` ✅\n* transform a stream of primitives into a stream of `Integer` objects (\"**Stream boxing**\") ✅\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>5-0-functional-programming</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>5-4-1-fun-prime-numbers</artifactId>\n\n\n</project>"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/OOSumOfPrimes.java",
    "content": "package com.bobocode.fp;\n\n/**\n * This examples demonstrates how to calculate the sum of prime numbers using Object-Oriented approach.\n * Please note that in order to perform this calculation in OOP-style we have to use mutable variables\n * (e.g. sumOfPrimes, i, primes). Implementing the same functionality in a functional style using Steam API and lambdas,\n * you will not create and/or modify variables.\n *\n * @author Taras Boychuk\n */\npublic class OOSumOfPrimes {\n    public static void main(String[] args) {\n        int sumOfPrimes = 0;\n        int primes = 0;\n        for (int i = 0; primes <= 20; i++) {\n            if (isPrime(i)) {\n                System.out.println(primes + \" : \" + i);\n                sumOfPrimes += i;\n                primes++;\n            }\n        }\n\n        System.out.println(\"Sum of first 20 primes: \" + sumOfPrimes);\n    }\n\n    private static boolean isPrime(int n) {\n        for (int i = 2; i < n; i++) {\n            if (n % i == 0) {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java",
    "content": "package com.bobocode.fp;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.IntConsumer;\nimport java.util.stream.IntStream;\n\n/**\n * {@link PrimeNumbers} provides an API to work with prime numbers. The implementation is based on the\n * {@link java.util.stream.IntStream} of prime numbers. That stream is used in all public methods on this class.\n * <p>\n * See {@link OOSumOfPrimes} for a reference\n * TODO: implement each method according to the javadoc and verify it by running {@link PrimeNumbersTest}\n * <p>\n * TODO: if you find this exercise valuable and you want to get more like it, <a href=\"https://www.patreon.com/bobocode\">\n *     please support us on Patreon</a>\n *\n * @author Taras Boychuk\n */\npublic class PrimeNumbers {\n    private PrimeNumbers() {\n    }\n\n    /**\n     * Generates an infinite int stream of prime numbers.\n     * The stream values are 2, 3, 5,... and so on.\n     *\n     * @return an infinite int stream of prime numbers\n     */\n    public static IntStream stream() {\n        throw new ExerciseNotCompletedException(); // todo: create an infinite stream of ints, then filter prime numbs\n    }\n\n    /**\n     * Generates an int stream of a certain amount of prime numbers.\n     * It is based on the {@link PrimeNumbers#stream()} but specifies the exact size of the stream.\n     *\n     * @return an int stream of prime numbers with a specified size\n     */\n    public static IntStream stream(int size) {\n        throw new ExerciseNotCompletedException(); // todo: use the prev to generate a stream method but limit its size\n    }\n\n    /**\n     * Calculates the sum on first n prime numbers.\n     * E.g. if n = 5, the result should be 2 + 3 + 5 + 7 + 11 = 28\n     *\n     * @param n the number of first prime numbers\n     * @return the sum of n prime numbers\n     */\n    public static int sum(int n) {\n        throw new ExerciseNotCompletedException(); // todo: use prev method and calculate the sum\n\n    }\n\n    /**\n     * Collects n first prime numbers.\n     *\n     * @return a list of collected prime numbers\n     */\n    public static List<Integer> list(int n) {\n        throw new ExerciseNotCompletedException(); // todo: collect prime numbers into the list\n    }\n\n    /**\n     * Find a prime number by index and then applies a provided consumer passing found prime number\n     *\n     * @param idx      the position of a prime number (index), starting from 0\n     * @param consumer a logic that should be applied to the found prime number\n     */\n    public static void processByIndex(int idx, IntConsumer consumer) {\n        throw new ExerciseNotCompletedException(); // todo: find an element in the stream by index and process it\n    }\n\n    /**\n     * Creates a list of n prime numbers and returns a map where all of those prime numbers are groped. The key represents\n     * an amount of digits and the value is a corresponding list of all prime numbers.\n     * <p>\n     * So if you will call this method for with argument 20, you will receive the following map:\n     * {1=[2, 3, 5, 7], 2=[11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]}\n     *\n     * @param n – the amount of prime numbers\n     * @return a map with prime number grouped by the amount of digits\n     */\n    public static Map<Integer, List<Integer>> groupByAmountOfDigits(int n) {\n        throw new ExerciseNotCompletedException(); // todo: group n prime numbers by the amount of digits\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/src/test/java/com/bobocode/fp/PrimeNumbersTest.java",
    "content": "package com.bobocode.fp;\n\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static java.util.stream.Collectors.toList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\n/**\n * A test class for {@link PrimeNumbers}.\n *\n * @author Taras Boychuk\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\nclass PrimeNumbersTest {\n\n    @Order(1)\n    @Test\n    void stream() {\n        var primeNumbersStream = PrimeNumbers.stream();\n\n        var primeList = primeNumbersStream\n                .limit(5)\n                .boxed()\n                .collect(toList());\n\n        assertThat(primeList).isEqualTo(List.of(2, 3, 5, 7, 11));\n    }\n\n    @Order(2)\n    @Test\n    void streamN() {\n        var primeNumbersStream = PrimeNumbers.stream(8);\n\n        var primeList = primeNumbersStream\n                .boxed()\n                .collect(toList());\n\n        assertThat(primeList).isEqualTo(List.of(2, 3, 5, 7, 11, 13, 17, 19));\n    }\n\n    @Order(3)\n    @ParameterizedTest\n    @CsvSource({\"0, 0\", \"1, 2\", \"2, 5\", \"3, 10\", \"4, 17\", \"5, 28\", \"10, 129\", \"20, 639\"})\n    void sum(int n, int sumOfPrimes) {\n        int result = PrimeNumbers.sum(n);\n\n        assertThat(result).isEqualTo(sumOfPrimes);\n    }\n\n    @Order(4)\n    @ParameterizedTest\n    @MethodSource(\"collectArgs\")\n    void collect(int n, List<Integer> primeNumbersList) {\n        List<Integer> result = PrimeNumbers.list(n);\n\n        assertThat(result).isEqualTo(primeNumbersList);\n    }\n\n    @Order(5)\n    @ParameterizedTest\n    @CsvSource({\"0, 2\", \"1, 3\", \"2, 5\", \"3, 7\", \"10, 31\", \"20, 73\", \"279, 1811\"})\n    void processByIndexFindsCorrectPrimeNumber(int index, int primeNumber) {\n        var list = new ArrayList<>();\n\n        PrimeNumbers.processByIndex(index, list::add);\n\n        assertThat(list.get(0)).isEqualTo(primeNumber);\n    }\n\n    static Stream<Arguments> collectArgs() {\n        return Stream.of(\n                arguments(1, List.of(2)),\n                arguments(2, List.of(2, 3)),\n                arguments(3, List.of(2, 3, 5)),\n                arguments(4, List.of(2, 3, 5, 7)),\n                arguments(5, List.of(2, 3, 5, 7, 11)),\n                arguments(10, List.of(2, 3, 5, 7, 11, 13, 17, 19, 23, 29))\n        );\n    }\n\n    @Order(6)\n    @ParameterizedTest\n    @MethodSource(\"groupByAmountOfDigitsArgs\")\n    void groupByAmountOfDigits(int count, Map<Integer, List<Integer>> digitsToPrimesMap) {\n        var result = PrimeNumbers.groupByAmountOfDigits(count);\n\n        assertThat(result).isEqualTo(digitsToPrimesMap);\n    }\n\n    static Stream<Arguments> groupByAmountOfDigitsArgs() {\n        return Stream.of(\n                arguments(1, Map.of(1, List.of(2))),\n                arguments(10, Map.of(\n                        1, List.of(2, 3, 5, 7),\n                        2, List.of(11, 13, 17, 19, 23, 29)\n                )),\n                arguments(20, Map.of(\n                        1, List.of(2, 3, 5, 7),\n                        2, List.of(11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71)\n                )),\n                arguments(30, Map.of(\n                                1, List.of(2, 3, 5, 7),\n                                2, List.of(11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97),\n                                3, List.of(101, 103, 107, 109, 113)\n                        )\n                )\n\n        );\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Java Functional Programming \n&nbsp;\n### Master Java functional features and write more consice and easy to parallel code 💪\n&nbsp;\n\n### Why should you care?\n⚡️ Nowadays **every Java developer uses some functional programming features in an everyday job**. So make sure you've \nchecked out the rest of the materials in this module and build strong skills using these language features! \n\nThe main idea is to keep Java OO language but **enable some functional programming features**. It does two things:\n* makes the code more concise\n* allows easier parallelization\n\nJava SE 8+ provides a rich API that enables functional programming features based on\n* Functional Interfaces\n* Lambdas\n* Stream API\n* Optional API\n\n### At the end of this module you will be able to\nWrite this\n```java\npublic List<Account> findAllGmailAccounts(List<Account> accounts) {\n        return accounts.stream()\n                .filter(a -> a.getEmail().endsWith(\"@gmail.com\"))\n                .collect(toList());\n}\n```\ninstead of this\n```java\npublic List<Account> findAllGmailAccounts(List<Account> accounts) {\n    List<Account> gmailAccounts = new ArrayList<>();\n        for (Account account : accounts) {\n            if (account.getEmail().endsWith(\"@gmail.com\")) {\n                gmailAccounts.add(account);\n            }\n        }\n    return gmailAccounts;\n}\n```\nAmong other you will be able to\n* use **Funtional Interfaces** and **Lambdas** in order to **pass around functions** like first-class citizens ✅\n* **process data collections** in a **concise** and **easy way to understand** using **Stream API** ✅\n* write **null-safe code** using **Optional API** ✅\n\n### Learn or skip ? \nThink you're cool enough to skip this topic? 😎 Hand on a sec...☝️ Can you easily understand and write lambdas like this?\n```java\nrunnable -> () -> {\n            Thread t = new Thread(runnable);\n            t.start();\n            return t;\n};\n```\nor stream pipelines like this ? 🧐\n```java\naccounts.stream()\n        .flatMap(a -> Stream.of(a.getFirstName(), a.getLastName()))\n        .map(String::toLowerCase)\n        .flatMapToInt(String::chars)\n        .mapToObj(c -> (char) c)\n        .collect(groupingBy(identity(), counting()));\n```\nNo worries if you don't! Be patient, **do the exercises in this module**, and you will be able to do not only this 👆.\n**You will understand and use functional programming techniques far beyond your expectations** 🔥\n"
  },
  {
    "path": "5-0-functional-programming/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>pom</packaging>\n    <modules>\n        <module>5-1-1-crazy-lambdas</module>\n        <module>5-2-1-crazy-streams</module>\n        <module>5-3-1-crazy-optionals</module>\n        <module>5-0-1-lambda-functions-map</module>\n        <module>5-0-2-stream-sum-of-squares</module>\n        <module>5-4-1-fun-prime-numbers</module>\n    </modules>\n\n    <parent>\n        <groupId>com.bobocode</groupId>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <artifactId>5-0-functional-programming</artifactId>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>java-fundamentals-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/CrazyLambdaExample.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\n/**\n * An example of crazy lambda that is hard but still important to understand\n */\npublic class CrazyLambdaExample {\n    public static void main(String[] args) {\n        Supplier<Function<Predicate<String>, Consumer<String>>> stringPredicateToConsumerFunctionSupplier =\n                getStringPredicateToConsumerFunctionSupplier();\n\n        Function<Predicate<String>, Consumer<String>> stringPredicateConsumerFunction = stringPredicateToConsumerFunctionSupplier.get();\n        Consumer<String> stringIsEmptyChecker = stringPredicateConsumerFunction.apply(String::isEmpty);\n        stringIsEmptyChecker.accept(\"\");\n    }\n\n    /**\n     * Returns the {@link Supplier} instance that supplies {@link Function} that receives a string {@link Predicate} as\n     * a parameter and returns string {@link Consumer}\n     *\n     * @return an instance of supplier\n     */\n    private static Supplier<Function<Predicate<String>, Consumer<String>>> getStringPredicateToConsumerFunctionSupplier() {\n        return () -> stringPredicate -> str -> System.out.println(stringPredicate.test(str));\n    }\n\n\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/FunctionComposition.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.function.IntUnaryOperator;\nimport java.util.function.Predicate;\n\n/**\n * One of functional programming techniques if a function composition. Having two different functions f(x) and g(x) in\n * math you would write f(g(x)) to compose those function. The same you can do in Java using method\n * {@link IntUnaryOperator#compose} or {@link IntUnaryOperator#andThen(IntUnaryOperator)}\n * <p>\n * In fact most predefined functional interfaces have similar capabilities.\n * E.g. {@link Function#compose(Function)} or {@link Predicate#and(Predicate)}\n */\npublic class FunctionComposition {\n    public static void main(String[] args) {\n        printSquareOfDoubleUsingFunctionComposition();\n        printStringIsBlankUsingPredicateComposition();\n    }\n\n    private static void printSquareOfDoubleUsingFunctionComposition() {\n        IntUnaryOperator squareFunction = a -> a * a; // s(x) = x * x\n        IntUnaryOperator doublerFunction = a -> 2 * a; // d(x) = 2 * x\n\n        IntUnaryOperator squareOfDoubleFunction = squareFunction.compose(doublerFunction); // s(d(x))\n\n        System.out.println(\"square(double(3)) = \" + squareOfDoubleFunction.applyAsInt(3));\n    }\n\n    private static void printStringIsBlankUsingPredicateComposition() {\n        Predicate<String> isEmptyPredicate = String::isEmpty;\n        Predicate<String> isNotNullPredicate = Objects::nonNull;\n\n        Predicate<String> isNotBlank = isNotNullPredicate.and(isEmptyPredicate.negate());\n\n        String str = \"Hi\";\n        System.out.println(\"String \\\"\" + str + \"\\\" is not blank? \" + isNotBlank.test(str));\n\n\n    }\n\n\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaAndMethodReference.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * Method reference is a shorthand for lambda expression that could be use in some cases for better readability.\n */\npublic class LambdaAndMethodReference {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printAccountsUsingLambda(accounts);\n        printAccountsUsingMethodReference(accounts);\n    }\n\n    private static void printAccountsUsingLambda(List<Account> accounts) {\n        processAccounts(accounts, a -> System.out.println(a));\n    }\n\n    private static void printAccountsUsingMethodReference(List<Account> accounts) {\n        processAccounts(accounts, System.out::println);\n    }\n\n    private static void processAccounts(List<Account> accounts, Consumer<Account> consumer) {\n        for (Account account : accounts){\n            consumer.accept(account);\n        }\n    }\n\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaComparatorExample.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.Comparator;\nimport java.util.List;\n\nimport static java.util.Comparator.comparing;\n\npublic class LambdaComparatorExample {\n\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n        sortUsingAnonymousClass(accounts);\n        sortUsingLambda(accounts);\n        sortUsingMethodReference(accounts);\n    }\n\n    private static void sortUsingAnonymousClass(List<Account> accounts) {\n        accounts.sort(new Comparator<Account>() {\n            @Override\n            public int compare(Account o1, Account o2) {\n                return o1.getFirstName().compareTo(o2.getFirstName());\n            }\n        });\n    }\n\n    private static void sortUsingLambda(List<Account> accounts) {\n        accounts.sort((a1, a2) -> a1.getFirstName().compareTo(a2.getFirstName()));\n    }\n\n    private static void sortUsingMethodReference(List<Account> accounts) {\n        accounts.sort(comparing(Account::getFirstName));\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaRunnableExample.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\npublic class LambdaRunnableExample {\n    public static void main(String[] args) {\n        sayHelloFromNewThread();\n        sayHelloFromNewThreadUsingLambda();\n    }\n\n\n    private static void sayHelloFromNewThread() {\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                System.out.println(\"Hello from new Thread!\");\n            }\n        };\n        Thread t = new Thread(runnable);\n        t.start();\n    }\n\n    private static void sayHelloFromNewThreadUsingLambda() {\n        Runnable runnable = () -> System.out.println(\"Hello Lambda!\");\n        Thread t = new Thread(runnable);\n        t.start();\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/MethodRefToUpperCaseExample.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.function.UnaryOperator;\n\n/**\n * A list of different examples of method reference usage.\n */\npublic class MethodRefToUpperCaseExample {\n    public static void main(String[] args) {\n        String s = \"Method reference insights\";\n\n        printUpperStringUsingFunction(s);\n        printUpperStringUsingUnaryOperation(s);\n        printUpperStringUsingStringSupplier(s);\n    }\n\n    /**\n     * This is the unbound method reference example. Since we used unbound method reference, the {@link Function} can\n     * receive a string as an input parameter.\n     */\n    private static void printUpperStringUsingFunction(String s) {\n        Function<String, String> upperCaseFunction = String::toUpperCase;\n\n        System.out.println(upperCaseFunction.apply(s));\n    }\n\n    /**\n     * This is the unbound method reference example. Since toUpperCase() receives and returns the same type\n     * {@link String}, we can easily replace {@link Function} with {@link UnaryOperator}\n     */\n    private static void printUpperStringUsingUnaryOperation(String s) {\n        UnaryOperator<String> upperCaseOperator = String::toUpperCase;\n\n        System.out.println(upperCaseOperator.apply(s));\n    }\n\n    /**\n     * This is the bound method reference example. We actually reference a method that is bound to concrete\n     * string instance. Since we reference a concrete string, there is no input parameter, only an output. In this case\n     * we can use {@link Supplier}\n     */\n    private static void printUpperStringUsingStringSupplier(String s) {\n        Supplier<String> stringSupplier = s::toUpperCase;\n\n        System.out.println(stringSupplier.get());\n    }\n\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/MethodReferenceExamples.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.function.IntBinaryOperator;\nimport java.util.function.IntUnaryOperator;\nimport java.util.function.UnaryOperator;\n\n/**\n * A list of method reference examples.\n */\npublic class MethodReferenceExamples {\n    public static void main(String[] args) {\n        printAbsUsingMethodReference(-23);\n        printSumUsingMethodReference(25, 50);\n        printUpperStringUsingMethodReference(\"Lambda is awesome!\");\n    }\n\n    private static void printAbsUsingMethodReference(int a) {\n        IntUnaryOperator absOperator = Math::abs;\n        int result = absOperator.applyAsInt(a);\n\n        System.out.println(\"abs(\" + a + \") = \" + result);\n    }\n\n    private static void printSumUsingMethodReference(int a, int b) {\n        IntBinaryOperator sumOperator = Math::addExact;\n        int result = sumOperator.applyAsInt(a, b);\n\n        System.out.println(\"\\n\" + a + \" + \" + b + \" = \" + result);\n    }\n\n    private static void printUpperStringUsingMethodReference(String s) {\n        UnaryOperator<String> upperOperation = String::toUpperCase;\n\n        System.out.println(\"\\n\" + s + \" -> \" + upperOperation.apply(s));\n    }\n\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/PredefinedInterfacePrimitives.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.function.DoubleSupplier;\nimport java.util.function.IntToDoubleFunction;\nimport java.util.function.LongBinaryOperator;\n\n/**\n * Always use special classes for Primitives\n */\npublic class PredefinedInterfacePrimitives {\n    public static void main(String[] args) {\n        printRandomDoubleUsingSupplier();\n        printLongSumUsingBinaryOperation(124124L, 132134L);\n        printSqrtUsingFunction(25);\n    }\n\n    private static void printRandomDoubleUsingSupplier() {\n        DoubleSupplier doubleSupplier = () -> ThreadLocalRandom.current().nextDouble();\n\n        System.out.println(\"Random double: \" + doubleSupplier.getAsDouble());\n    }\n\n    private static void printLongSumUsingBinaryOperation(long a, long b) {\n        LongBinaryOperator sumOperator = Long::sum;\n\n        System.out.println(\"\\n\" + a + \" \" + b + \" = \" + sumOperator.applyAsLong(a, b));\n    }\n\n    private static void printSqrtUsingFunction(int a) {\n        IntToDoubleFunction sqrtFunction = Math::sqrt;\n\n        System.out.println(\"\\nsqrt(\" + a + \") = \" + sqrtFunction.applyAsDouble(a));\n    }\n\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/PredefinedInterfacesExamples.java",
    "content": "package com.bobocode.lambdas.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.function.*;\nimport java.util.stream.IntStream;\n\n/**\n * A list of predefined interfaces examples.\n */\npublic class PredefinedInterfacesExamples {\n    public static void main(String[] args) {\n        printEmailUsingAccountConsumer();\n        printARandomNumberUsingIntegerSupplier();\n        calculate3xValueUsingIntegerFunction();\n        checkIfNumberIsPositiveUsingIntegerPredicate();\n        verifyGoogleEmailUsingAccountPredicate();\n        printPrimeNumbersUsingIntegerPredicate();\n    }\n\n    private static void printARandomNumberUsingIntegerSupplier() {\n        IntSupplier integerSupplier = () -> ThreadLocalRandom.current().nextInt(1000);\n\n        System.out.println(\"\\nNext random value: \" + integerSupplier.getAsInt());\n    }\n\n    private static void printEmailUsingAccountConsumer() {\n        Consumer<Account> accountConsumer = acc -> System.out.println(\"\\nAccount email: \" + acc.getEmail());\n        Account account = Accounts.generateAccount();\n\n        accountConsumer.accept(account);\n    }\n\n    private static void calculate3xValueUsingIntegerFunction() {\n        IntUnaryOperator tripleFunction = n -> 3 * n;\n        int a = 12;\n\n        System.out.println(\"\\n3 * \" + a + \" = \" + tripleFunction.applyAsInt(a));\n    }\n\n    private static void checkIfNumberIsPositiveUsingIntegerPredicate() {\n        IntPredicate isPositive = n -> n > 0;\n        int b = ThreadLocalRandom.current().nextInt();\n\n        System.out.println(\"\\n\" + b + \" is \" + (isPositive.test(b) ? \"positive\" : \"negative\"));\n    }\n\n    private static void verifyGoogleEmailUsingAccountPredicate() {\n        Account account = Accounts.generateAccount();\n        Predicate<Account> isGmailUser = a -> a.getEmail().endsWith(\"@gmail.com\");\n\n        System.out.println(\"\\n\" + account.getEmail() + \" is \"\n                + (isGmailUser.test(account) ? \"\" : \"not\") + \" a Google email.\");\n    }\n\n    private static void printPrimeNumbersUsingIntegerPredicate() {\n        IntPredicate isPrime = n -> IntStream.range(2, n).noneMatch(i -> n % i == 0);\n        System.out.println();\n        IntStream.rangeClosed(1, 25)\n                .forEach(i -> System.out.printf(\"%3d %10s\\n\", i, (isPrime.test(i) ? \" is prime\" : \"\")));\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalCreation.java",
    "content": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.Optional;\n\npublic class OptionalCreation {\n    public static void main(String[] args) {\n        Account account = Accounts.generateAccount();\n\n        Optional<Account> optionalAccount = Optional.of(account);\n\n        Optional<Account> optionalNullableAccount = Optional.ofNullable(account);\n\n        Optional<Account> optionalEmptyAccount = Optional.empty();\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalExamples.java",
    "content": "package com.bobocode.optionals.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\npublic class  OptionalExamples {\n    private static Account DEFAULT_ACCOUNT = Accounts.generateAccount();\n\n    public static void main(String[] args) {\n        Account account = Accounts.generateAccount();\n        Optional<Account> optionalAccount = Optional.of(account);\n\n        printAccountUsingWrongApproach(optionalAccount);\n        printAccountUsingRightApproach(optionalAccount);\n\n        printAccountOrDefault(optionalAccount);\n        printAccountOrRandomLazily(optionalAccount);\n    }\n\n    /**\n     * It is not correct to process to process {@link Optional} values in the old imperative way, as it is shown below\n     */\n    private static void printAccountUsingWrongApproach(Optional<Account> optionalAccount) {\n        if (optionalAccount.isPresent()) {\n            Account account = optionalAccount.get();\n            System.out.println(account);\n        }\n    }\n\n    /**\n     * The right way is to pass a consumer that will be used in case {@link Optional} value is not empty\n     */\n    private static void printAccountUsingRightApproach(Optional<Account> optionalAccount) {\n        optionalAccount.ifPresent(System.out::println);\n    }\n\n    /**\n     * or add a default value. Now you're safely trying to get an account instance, because in case it's null\n     * you'll get a default value\n     */\n    private static void printAccountOrDefault(Optional<Account> optionalAccount) {\n        Account account = optionalAccount.orElse(DEFAULT_ACCOUNT);\n        System.out.println(account);\n    }\n\n    /**\n     * The version with {@link Supplier} should be used in case getting default value requires\n     * additional resources. In this case default instance will be only created if optional account is empty\n     */\n    private static void printAccountOrRandomLazily(Optional<Account> optionalAccount) {\n        Account account = optionalAccount.orElseGet(() -> Accounts.generateAccount());\n        System.out.println(account);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalImperativeVsDeclarativeCheck.java",
    "content": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.Optional;\n\npublic class OptionalImperativeVsDeclarativeCheck {\n    public static void main(String[] args) {\n        Account account = Accounts.generateAccount();\n        Optional<Account> optionalAccount = Optional.of(account);\n\n        printAccount(account);\n        printOptionalAccount(optionalAccount);\n    }\n\n    /**\n     * An if statement is a classic example of imperative check\n     */\n    private static void printAccount(Account account) {\n        if (account != null) {\n            System.out.println(account);\n        } else {\n            System.out.println(\"No such element exists!\");\n        }\n    }\n\n    /**\n     * This is an example of declarative check. You don't write an if statement yourself, it is performed inside the\n     * method ifPresentOrElse()\n     */\n    private static void printOptionalAccount(Optional<Account> optionalAccount) {\n        optionalAccount.ifPresentOrElse(System.out::println,\n                () -> System.out.println(\"No such element exists!\"));\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalMapping.java",
    "content": "package com.bobocode.optionals.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\nimport com.bobocode.model.CreditAccount;\n\nimport java.math.BigDecimal;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\npublic class OptionalMapping {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n        List<CreditAccount> creditAccounts = Collections.singletonList(new CreditAccount(BigDecimal.valueOf(1000)));\n        Optional<Account> optionalAccount = accounts.stream().findAny();\n\n        printBalanceUsingNullCheck(optionalAccount.get());\n        printBalanceUsingOptionalMapping(optionalAccount);\n\n        printBalanceOfFirstGoogleAccount(accounts);\n        printCreditBalanceOfFirstGoogleAccount(creditAccounts);\n    }\n\n    /**\n     * Hierarchical relation with potential null value, like an account that can be null, has a balance\n     * field that also can be null\n     */\n    private static void printBalanceUsingNullCheck(Account account) {\n        if (account != null) {\n            if (account.getBalance() != null) {\n                System.out.println(account.getBalance());\n            }\n        }\n    }\n\n    /**\n     * When you want to access a nullable value of account, you can use method map(), that receives a mapper which\n     * transform your nullable value into an optional value\n     */\n    private static void printBalanceUsingOptionalMapping(Optional<Account> optionalAccount) {\n        optionalAccount.map(Account::getBalance).ifPresent(System.out::println);\n    }\n\n    /**\n     * An example, when you process a stream and find an optional element, with an relation that can be null\n     */\n    private static void printBalanceOfFirstGoogleAccount(List<Account> accounts) {\n        accounts.stream()\n                .filter(a -> a.getEmail().endsWith(\"google.com\"))\n                .findFirst()\n                .map(Account::getBalance)\n                .ifPresent(System.out::println);\n    }\n\n    /**\n     * When you use map() it wraps a value within Optional container, and if that field is already an {@link Optional},\n     * you get Optional<Optional<T>>. To avoid this use flatMap()\n     */\n    private static void printCreditBalanceOfFirstGoogleAccount(List<CreditAccount> creditAccounts) {\n        creditAccounts.stream()\n                .filter(a -> a.getEmail().endsWith(\"google.com\"))\n                .filter(a -> a.getBalance().compareTo(BigDecimal.valueOf(90000)) > 0)\n                .findFirst()\n                .flatMap(CreditAccount::getCreditBalance)\n                .ifPresent(System.out::println);\n\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalPrimitives.java",
    "content": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.OptionalDouble;\n\npublic class OptionalPrimitives {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        OptionalDouble minBalance = findMinBalance(accounts); // TODO: NEVER USE Optional<Double>\n        System.out.println(\"Min balance is \" + minBalance.orElse(0));\n    }\n\n    /**\n     * Always use special classes for optional primitives. See also {@link java.util.OptionalInt},\n     * {@link java.util.OptionalLong}\n     *\n     */\n    private static OptionalDouble findMinBalance(List<Account> accounts) {\n        return accounts.stream()\n                .map(Account::getBalance)\n                .mapToDouble(BigDecimal::doubleValue)\n                .min();\n    }\n\n\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalReturningMethod.java",
    "content": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic class OptionalReturningMethod {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(20);\n        Optional<Account> luckyGuy = findLuckyGuy(accounts);\n        printResult(luckyGuy);\n\n    }\n\n    /**\n     * If in some cases a method cannot produce a retuning value, use an {@link Optional}\n     * as a returning-value in favor of null\n     */\n    private static Optional<Account> findLuckyGuy(List<Account> accounts) {\n        int luckyIndex = ThreadLocalRandom.current().nextInt(accounts.size() * 3);\n        if (luckyIndex < accounts.size()) {\n            return Optional.of(accounts.get(luckyIndex));\n        } else {\n            // return null; // TODO: NEVER EVER RETURN null FROM AN OPTIONAL-RETURNING METHOD\n            return Optional.empty();\n        }\n    }\n\n    /**\n     * Instead of using imperative if-else statement, use ifPresentOrElse() method\n     */\n    private static void printResult(Optional<Account> optionalAccount) {\n        optionalAccount.ifPresentOrElse(System.out::println, () -> System.out.println(\"No luck! Try again...\"));\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalSearchByEmailExample.java",
    "content": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.List;\nimport java.util.Optional;\n\npublic class OptionalSearchByEmailExample {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        Account account = getAccountByEmail(accounts, \"jsushi@gmail.com\");\n\n        printAccount(account);\n    }\n\n    private static Optional<Account> findAccountByEmail(List<Account> accounts, String email) {\n        return accounts.stream()\n                .filter(a -> a.getEmail().equals(email))\n                .findAny();\n    }\n\n    private static Account getAccountByEmail(List<Account> accounts, String email) {\n        return findAccountByEmail(accounts, email)\n                .orElseThrow(NoSuchElementException::new);\n    }\n\n    /**\n     * Wrong way of using Optional\n     */\n    private static Account getAccountByEmailImperatively(List<Account> accounts, String email) {\n        Optional<Account> optionalAccount = findAccountByEmail(accounts, email);\n        if (optionalAccount.isPresent()) {\n            return optionalAccount.get();\n        } else {\n            throw new NoSuchElementException();\n        }\n\n    }\n\n    private static void printAccount(Account account) {\n        System.out.println(\"This account belongs to \" + account.getFirstName() +\n                \" \" + account.getLastName());\n    }\n\n    static class NoSuchElementException extends RuntimeException {\n\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalStream.java",
    "content": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.CreditAccount;\n\nimport java.util.List;\nimport java.util.Optional;\n\npublic class OptionalStream {\n    public static void main(String[] args) {\n        List<CreditAccount> creditAccounts = Accounts.generateCreditAccountList(10);\n\n        printExistingCreditBalancesJava8(creditAccounts);\n        printExistingCreditBalancesJava9(creditAccounts);\n    }\n\n    private static void printExistingCreditBalancesJava8(List<CreditAccount> creditAccounts) {\n        creditAccounts.stream()\n                .map(CreditAccount::getCreditBalance)\n                .filter(Optional::isPresent)\n                .map(Optional::get)\n                .forEach(System.out::println);\n    }\n\n    private static void printExistingCreditBalancesJava9(List<CreditAccount> creditAccounts) {\n        creditAccounts.stream()\n                .map(CreditAccount::getCreditBalance)\n                .flatMap(Optional::stream)\n                .forEach(System.out::println);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/> Optional tutorial\n\nThis is the tutorial on `java.util.Optional<T>`\n### Pre-conditions ❗\nYou're supposed to be familiar with OOP, have basic knowledge of JDK, and be able to write Java code. \n### Related exercises 💪\n* \n### See also 🔽\n* [Tutorial on Lamdbas](to do)\n* [Tutorial on Stream API](to do)\n##\n\nWhen writing a **method that is not able to return a value** in some cases, there are three options:\n* return `null`\n* throw an exception\n* return `Optinonal<T>`\n\nReturning `null` is obliously the most **error prone** approach. If you want to be safe, you should somehow **force clients** of your \nmethod **to handle the possible lack of value**. For that purpose you can use **checked exceptoins**. Although this approach is \nsafe it has two disatvantages: thowing exceptoin is an **expansive operatoin**, and it brings **addtional boilerplate**. The \nbetter approach is to use `Optional<T>`, which is **safe** and **concise**.\n\n`Optional<T>` is a **like a collection that can hold at most one value**. It provides usefull methods to **replace imperative \nold-style null-checks**. It also contains convenient **methods for working with Stream API**. In general, Optional API follows the \nsimilar functional-style approach as streams. \n\n### Best practices\n* **prefer Optional-returning method** in case the lack of value is possible\n* **return Optional from methods** (e.g. getter), **do not wrap instance fields**\n* prefer methods like `Optional#ifPresent()` over imperative `if` statements\n* **never return `null`** from an Optional-returning method\n* always use special supplier-based method `Optional#orElseGet()` in case it is expensive to compute default value\n* do not wrap with optional arrays, collections, streams, and maps\n* **use special classes for primitive types** (e.g. `OptionalInt`)\n* **avoid using Optional for performance-critical cases**\n\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/ImperativeVsDeclarativeFiltering.java",
    "content": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static java.util.stream.Collectors.toList;\n\npublic class ImperativeVsDeclarativeFiltering {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        findAllGmailAccountsImperatively(accounts);\n        findAllGmailAccountsDeclaratively(accounts);\n\n    }\n\n    private static List<Account> findAllGmailAccountsImperatively(List<Account> accounts) {\n        List<Account> gmailAccounts = new ArrayList<>();\n        for (Account account : accounts) {\n            if (account.getEmail().endsWith(\"@gmail.com\")) {\n                gmailAccounts.add(account);\n            }\n        }\n        return gmailAccounts;\n    }\n\n    private static List<Account> findAllGmailAccountsDeclaratively(List<Account> accounts) {\n        return accounts.stream()\n                .filter(a -> a.getEmail().endsWith(\"@gmail.com\"))\n                .collect(toList());\n    }\n\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/ImperativeVsDeclarativeMax.java",
    "content": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.List;\n\nimport static java.util.Comparator.comparing;\n\npublic class ImperativeVsDeclarativeMax {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printAccountWithMaxBalanceImperatively(accounts);\n        printAccountWithMaxBalanceDeclaratively(accounts);\n    }\n\n    private static void printAccountWithMaxBalanceDeclaratively(List<Account> accounts) {\n        accounts.stream()\n                .max(comparing(Account::getBalance))\n                .ifPresentOrElse(System.out::println,\n                        () -> System.out.println(\"No accounts found!\"));\n\n    }\n\n    private static void printAccountWithMaxBalanceImperatively(List<Account> accounts) {\n        if (accounts.size() > 0) {\n            Account accountWithMaxBalance = accounts.get(0);\n            for (Account account : accounts) {\n                if (account.getBalance().compareTo(accountWithMaxBalance.getBalance()) > 0) {\n                    accountWithMaxBalance = account;\n                }\n            }\n            System.out.println(accountWithMaxBalance);\n        } else {\n            System.out.println(\"No accounts found!\");\n        }\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/> Stream API tutorial\n\nThis is the tutorial on Stream API and functional programming techniques\n### Pre-conditions ❗\nYou're supposed to be familiar with OOP, have basic knowledge of JDK, and be able to write Java code. \n### Related exercises 💪\n* [Account analytics](to do)\n* [File Reader](to do)\n* [File Stats](to do)\n### See also 🔽\n* [Tutorial on Lambdas](to do)\n* [Tutorial on Optional](to do)\n##\n*Stream API* provide an ability to **process sequences of data elements in a declarative way** and **simplify the task of \nperforming operations in parallel**\n\nThe simplest example is the task of filtering a collection. Using **imperative** old-style approach we specify **HOW the task \nshould be done**. E.g. dealing with iteration, and storing each element in a new `ArrayList`. This way of processing is also \ncalled *external iteration*. *Stream API* and it's **declarative** approach allows us to specify **WHAT should be done**, without \nactually dealing with iteration and elements. This approach is also called *internal iteration*. \n\nImperative style:\n```java\n        List<Account> gmailAccounts = new ArrayList<>();\n        for (Account account : accounts) {\n            if (account.getEmail().endsWith(\"@gmail.com\")) {\n                gmailAccounts.add(account);\n            }\n        }\n```\nDeclarative style using *Stream API*:\n```java\n        List<Account> gmailAccounts = accounts.stream()\n                .filter(a -> a.getEmail().endsWith(\"@gmail\"))\n                .collect(toList());\n```\n\n### Best practices\n* **use streams** wheneve it can make the code clear and concise\n* use **clear names for lambda parameters** in stream pipelines\n* **use helper methods** for better readability\n* be carefull using a stream of chars\n* **prefer pure functions** for stream operations, **avoid side-effect function**\n* always use **static import** for better readability\n* **always use `joining()` collector** for `CharSequence` elements to avoid performance issues\n* prefer collections and not stream as method return-type\n* use **parallel stream only when it's critical**, when you know that it helps, and **always measure it using real data**\n* **ALWAYS USE SPECIAL CLASSES FOR PRIMITIVES.** (E.g. `IntStream` instead of `Stream<Integer>`)\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamAdditionalFeatures.java",
    "content": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.math.BigDecimal;\nimport java.util.DoubleSummaryStatistics;\nimport java.util.List;\nimport java.util.Set;\n\nimport static java.util.stream.Collectors.toSet;\n\n/**\n * Stream API provides a lot of useful features features that help you process data even more concise.\n */\npublic class StreamAdditionalFeatures {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printSortedFirstNames(accounts);\n        printThirdAndFourthFirstNames(accounts);\n        printBalanceStatistic(accounts);\n        printFirstNamesAndCollectEmails(accounts);\n    }\n\n    private static void printSortedFirstNames(List<Account> accounts) {\n        System.out.println(\"Sorted first names: \");\n        accounts.stream()\n                .map(Account::getFirstName)\n                .sorted()\n                .forEach(System.out::println);\n    }\n\n    /**\n     * Stream API allow you to skip some elements, and limit the number of elements in the stream\n     */\n    private static void printThirdAndFourthFirstNames(List<Account> accounts) {\n        System.out.println(\"\\nThird and fourth first names: \");\n        accounts.stream()\n                .map(Account::getFirstName)\n                .sorted()\n                .skip(2)\n                .limit(2)\n                .forEach(System.out::println);\n    }\n\n    private static void printBalanceStatistic(List<Account> accounts) {\n        DoubleSummaryStatistics balanceStatistic = accounts.stream()\n                .map(Account::getBalance)\n                .mapToDouble(BigDecimal::doubleValue)\n                .summaryStatistics();\n        System.out.println(\"\\nAccounts balance statistic: \" + balanceStatistic);\n    }\n\n    /**\n     * Since forEach() is a terminal operation, you can not continue working with stream when it's performed.\n     * That's why Stream API provides another method, that has the similar capabilities but does not terminate a stream\n     */\n    private static void printFirstNamesAndCollectEmails(List<Account> accounts) {\n        System.out.println(\"\\nAccount first names: \");\n        Set<String> emailsSet = accounts.stream()\n                .peek(a -> System.out.println(a.getFirstName()))\n                .map(Account::getEmail)\n                .collect(toSet());\n        System.out.println(\"\\nEmails set: \" + emailsSet);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamBasics.java",
    "content": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.time.Month;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport static java.util.stream.Collectors.toList;\n\n/**\n * {@link Stream} is not a data collection by itself. It's an API that allows to perform bulk operation on top of some existing\n * data collection in a declarative way.\n * <p>\n * {@link Stream} provides a lot of useful methods to work with data sequences. All intermediate operation produce a new\n * stream. Those operations are performed lazily. E.g. the iteration on elements and all intermediate operations\n * are performed only when terminal operation is called. After that stream cannot be reused.\n */\n\n\npublic class StreamBasics {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n        Stream<Account> accountStream = accounts.stream(); // you can create stream directly from a collection object\n\n        List<Account> augustAccounts = accountStream.\n                filter(a -> a.getCreationDate().getMonth().equals(Month.AUGUST)) // 'filter' is an intermediate operation\n                .collect(toList());// 'collect' is a terminal operation\n\n        System.out.println(\"August accounts: \" + augustAccounts);\n        // accountStream.forEach(System.out::println); // stream cannot be reused. You'll get IllegalStateException\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamCollecting.java",
    "content": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.time.Month;\nimport java.util.List;\nimport java.util.Map;\n\nimport static java.util.stream.Collectors.*;\n\n/**\n * StreamCollecting stream elements with method collect() that receives a {@link java.util.stream.Collector} instance as input\n * parameter. Method collect() transforms a stream into a real collection like {@link List}, or a {@link Map}.\n * A {@link java.util.stream.Collector} is a complex structure, that describes a collecting logic. It provides\n * an instruction about how elements should collected, and what collection should be used.\n * <p>\n * The simplest example is collecting all elements into a list. However this mechanism is very powerful, and allows\n * to perform various complex data transformations. E.g. \"Group all account by it's birthday month\"\n */\npublic class StreamCollecting {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printAllGmailAccounts(accounts);\n        printAccountsByItsBirthdaysMonth(accounts);\n        printAccountFirstNamesByItsBirthdaysMonth(accounts);\n        printFirstNamesSeparatingGmailAccountsAndOthers(accounts);\n        printCommaSeparatedNames(accounts);\n        printNamesSeparatedGmailAccountAndOthersAndGroupedByBirthdayMonth(accounts);\n    }\n\n    private static void printAllGmailAccounts(List<Account> accounts) {\n        List<String> accountEmails = accounts.stream()\n                .map(Account::getEmail)\n                .filter(email -> email.endsWith(\"gmail.com\"))\n                .collect(toList());\n\n        System.out.println(\"\\nGmail accounts list: \" + accountEmails);\n\n    }\n\n    private static void printAccountsByItsBirthdaysMonth(List<Account> accounts) {\n        Map<Month, List<Account>> accountsByBirthdayMonthMap = accounts.stream()\n                .collect(groupingBy(account -> account.getBirthday().getMonth()));\n\n        System.out.println(\"\\nGroup accounts by birthday month: \" + accountsByBirthdayMonthMap);\n    }\n\n    private static void printAccountFirstNamesByItsBirthdaysMonth(List<Account> accounts) {\n        Map<Month, List<String>> accountFirstNamesBirthdayMonth = accounts.stream()\n                .collect(groupingBy(a -> a.getBirthday().getMonth(), mapping(Account::getFirstName, toList())));\n\n        System.out.println(\"\\nGroup account first names by birthday month: \" + accountFirstNamesBirthdayMonth);\n    }\n\n    private static void printFirstNamesSeparatingGmailAccountsAndOthers(List<Account> accounts) {\n        Map<Boolean, List<String>> googleEmailAccounts = accounts.stream()\n                .collect(partitioningBy(a -> a.getEmail().endsWith(\"gmail.com\"), mapping(Account::getFirstName, toList())));\n\n        System.out.println(\"\\nSeparate gmail accounts from others: \" + googleEmailAccounts);\n\n    }\n\n    /**\n     * Please always use joining() method to avoid performance issues when concatenating {@link String}\n     */\n    private static void printCommaSeparatedNames(List<Account> accounts) {\n\n        String concatenatedName = accounts.stream()\n                .map(Account::getFirstName)\n                .collect(joining(\", \"));\n\n        System.out.println(\"\\nComma-separated names: \" + concatenatedName);\n    }\n\n    /**\n     * Use streams carefully. Always keep the logic simple and clear. In case it becomes too complex, think how to\n     * simplify it use imperative style\n     */\n    private static void printNamesSeparatedGmailAccountAndOthersAndGroupedByBirthdayMonth(List<Account> accounts) {\n        Map<Boolean, Map<Month, List<String>>> accountByNameLengthByBirthdayMonth = accounts.stream()\n                .collect(partitioningBy(a -> a.getFirstName().length() > 4,\n                        groupingBy(a -> a.getBirthday().getMonth(),\n                                mapping(Account::getFirstName, toList()))));\n\n        System.out.println(\"\\nNames of gmail account owners and others groped by birthday month: \" +\n                accountByNameLengthByBirthdayMonth);\n\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamFiltering.java",
    "content": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\npublic class StreamFiltering {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printAccountsThatHaveGoogleEmail(accounts);\n    }\n\n    /**\n     * To filter accounts we use an instance of @{@link Predicate} that checks if an email is gmail based\n     * @param accounts\n     */\n    private static void printAccountsThatHaveGoogleEmail(List<Account> accounts) {\n        accounts.stream()\n                .filter(a -> a.getEmail().endsWith(\"gmail.com\"))\n                .forEach(System.out::println);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamMapping.java",
    "content": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport static java.util.function.Function.identity;\nimport static java.util.stream.Collectors.counting;\nimport static java.util.stream.Collectors.groupingBy;\n\npublic class StreamMapping {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printAccountEmails(accounts);\n        printCharacterCounts();\n    }\n\n    /**\n     * StreamMapping elements, method map() receives a @{@link java.util.function.Function}. This function transforms(maps)\n     * each element of the stream into another value (transforms each account object into its email). Method map()\n     * produce a new stream @{@link Stream} of @{@link String} (stream of emails)\n     */\n    private static void printAccountEmails(List<Account> accounts) {\n        accounts.stream()\n                .map(Account::getEmail)\n                .forEach(System.out::println);\n\n    }\n\n    private static void printCharacterCounts() {\n        String text = getSomeText();\n        Map<Character, Long> characterCountMap = collectCharactersCountFromText(text);\n        System.out.println(characterCountMap);\n    }\n\n    /**\n     * Count number of occurrences for each letter in each account first and last names\n     * flatMap() is used to transform Stream<Stream<T>> into Stream<T>\n     * Not you see the problem, that Java 8 doesn't provide a primitive stream API for characters\n     */\n    private static Map<Character, Long> collectCharactersCountFromText(String text) {\n        return text.chars()\n                .mapToObj(a -> (char) a)\n                .filter(s -> s != ' ')\n                .collect(groupingBy(identity(), counting()));\n\n    }\n\n    private static String getSomeText() {\n        return \"Stream pipeline results may be nondeterministic or incorrect if the behavioral parameters \" +\n                \"to the stream operations are stateful. A stateful lambda (or other object implementing \" +\n                \"the appropriate functional interface) is one whose result depends on any state \" +\n                \"which might change during the execution of the stream pipeline.\";\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamParallelProcessing.java",
    "content": "package com.bobocode.streams.tutorial;\n\n\nimport java.util.function.LongPredicate;\nimport java.util.stream.LongStream;\n\npublic class StreamParallelProcessing {\n    static final long STREAM_SIZE = 100_000_000;\n    static final int N = 10;\n\n    public static void main(String[] args) {\n        LongPredicate isDivisibleBySeven = n -> n % 7 == 0;\n\n        System.out.println(\"Sequential processing\");\n        performNTimes(N, () -> LongStream.range(1, STREAM_SIZE)\n                .filter(isDivisibleBySeven)\n                .count());\n\n        System.out.println(\"\\nParallel processing\");\n        performNTimes(N, () -> LongStream.range(1, STREAM_SIZE)\n                .parallel()\n                .filter(isDivisibleBySeven)\n                .count());\n\n    }\n\n    static void performNTimes(int n, Runnable r) {\n        LongStream.range(0, n).forEach(i -> {\n                    long start = System.nanoTime();\n                    r.run();\n                    System.out.println((System.nanoTime() - start) / 1_000_000 + \" ms\");\n                }\n        );\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamPrimitives.java",
    "content": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.List;\nimport java.util.stream.LongStream;\nimport java.util.stream.Stream;\n\npublic class StreamPrimitives {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printLongValues();\n        printMaxBalance(accounts);\n    }\n\n    /**\n     * {@link LongStream} is a stream of primitive long values.\n     * It can also be boxed into a {@link Stream} of {@link Long} values. PLEASE NOTE, that you should always use\n     * primitive streams whenever it's possible to avoid performance issues\n     */\n    private static void printLongValues() {\n        LongStream longStream = LongStream.of(1, 2, 3, 4, 5);\n        Stream<Long> boxedLongStream = longStream.boxed();\n        boxedLongStream.forEach(System.out::println);\n    }\n\n    /**\n     * This method creates a {@link Stream} of {@link Account} and then maps it to stream of doubles. To transform stream\n     * of objects like an {@link Account} into a stream of primitive doubles you should use special mapper method\n     * mapToDouble(), which creates an instance of {@link java.util.stream.DoubleStream}\n     */\n    private static void printMaxBalance(List<Account> accounts) {\n        double maxBalance = accounts.stream()\n                .mapToDouble(a -> a.getBalance().doubleValue())\n                .max().getAsDouble();\n\n        System.out.println(maxBalance);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamReducing.java",
    "content": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Stream reducing is performed with method reduce(), that repeatedly process stream elements to provide a single value\n */\npublic class StreamReducing {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printTotalBalance(accounts);\n        printMaxBalance(accounts);\n    }\n\n    private static void printTotalBalance(List<Account> accounts) {\n        BigDecimal totalAmount = accounts.stream()\n                .map(Account::getBalance)\n                .reduce(BigDecimal.ZERO, BigDecimal::add);\n\n        System.out.println(\"Total balance is $\" + totalAmount);\n    }\n\n    /**\n     * Please note that reduce() was use just an example. The better way to find max balance is to use\n     * accounts.stream().max(comparing(Account::getBalance))\n     */\n    private static void printMaxBalance(List<Account> accounts) {\n        Optional<BigDecimal> maxBalanceValue = accounts.stream()\n                .map(Account::getBalance)\n                .reduce(BigDecimal::max);\n\n        maxBalanceValue.ifPresent(balance -> System.out.println(\"Max balance is $\" + balance));\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamSideEffectFilteringExample.java",
    "content": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class StreamSideEffectFilteringExample {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        List<Account> gmailAccounts = new ArrayList<>();\n\n        accounts.stream()\n                .filter(a -> a.getEmail().endsWith(\"@gmail.com\"))\n                .forEach(gmailAccounts::add);\n    }\n}\n"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamWhileExample.java",
    "content": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport java.util.List;\n\npublic class StreamWhileExample {\n    public static void main(String[] args) {\n        List<Account> accounts = Accounts.generateAccountList(10);\n\n        printAllEmails(accounts);\n        printAllEmailsWhileGmail(accounts);\n        printAllEmailsWhileNotGmail(accounts);\n    }\n\n    private static void printAllEmails(List<Account> accounts) {\n        System.out.println(\"Whole emails list:\");\n        accounts.stream()\n                .map(Account::getEmail)\n                .forEach(System.out::println);\n    }\n\n    private static void printAllEmailsWhileGmail(List<Account> accounts) {\n        System.out.println(\"\\nAll while gmail:\");\n        accounts.stream()\n                .takeWhile(a -> a.getEmail().endsWith(\"@gmail.com\"))\n                .map(Account::getEmail)\n                .forEach(System.out::println);\n    }\n\n    private static void printAllEmailsWhileNotGmail(List<Account> accounts) {\n        System.out.println(\"\\nAll while not gmail:\");\n        accounts.stream()\n                .dropWhile(a -> a.getEmail().endsWith(\"@gmail.com\"))\n                .map(Account::getEmail)\n                .forEach(System.out::println);\n    }\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Stack exercise :muscle:\nImprove your TDD skill implementing Stack\n\n### Task\n**Stack** is last in, first out (LIFO) collection of elements. Your job is to implement the interface `Stack`\n using TDD discipline\n \n### Pre-conditions :heavy_exclamation_mark:\nYou're supposed to know [The Three laws of TDD](https://github.com/bobocode-projects/java-fundamentals-exercises#the-three-laws-of-tdd) relink,\n  be familiar with Stack data structure, and be able to write Java code\n\n### How to start :question:\n* Just clone the repository and start implementing `Stack` interface following *three laws of TDD*\n* If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)\n* Don't worry if you got stuck, checkout the [exercise/completed](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/exercise/completed) branch and see the final implementation\n \n### Related materials :information_source:\n * [Як виробити звичку писати тести? (Bobocode channel <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=20/>)](https://youtu.be/L_CiX9C51BI)\n * [Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))\n * [The Three Laws of TDD](https://www.youtube.com/watch?v=qkblc5WRn-U&t=3476s)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>6-0-test-driven-development</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>6-1-1-stack</artifactId>\n\n\n</project>"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/LinkedStack.java",
    "content": "package com.bobocode.tdd;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\npublic class LinkedStack<T> implements Stack<T> {\n    @Override\n    public void push(T element) {\n        throw new ExerciseNotCompletedException(); // todo\n    }\n    \n    @Override\n    public T pop() {\n        throw new ExerciseNotCompletedException(); // todo\n    }\n\n    @Override\n    public int size() {\n        throw new ExerciseNotCompletedException(); // todo\n    }\n\n    @Override\n    public boolean isEmpty() {\n        throw new ExerciseNotCompletedException(); // todo\n    }\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/Stack.java",
    "content": "package com.bobocode.tdd;\n\n/**\n *\n * Stack is a data structure that follows \"last in, first out\" rule (LIFO).\n */\npublic interface Stack<T> {\n    /**\n     * Adds an element to the begining of the stack.\n     *\n     * @param element the element to add\n     */\n    void push(T element);\n\n    /**\n     * Retrieves and removes stack head.\n     *\n     * @return an element that was retrieved from the head or null if stack is empty\n     */\n    T pop();\n\n    /**\n     * Returns a size of the stack.\n     *\n     * @return an integer value that is a size of stack\n     */\n    int size();\n\n    /**\n     * Checks if the stack is empty.\n     *\n     * @return {@code true} if the stack is empty, returns {@code false} if it's not\n     */\n    boolean isEmpty();\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/src/test/java/com/bobocode/tdd/StackTest.java",
    "content": "package com.bobocode.tdd;\n\npublic class StackTest {\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/README.MD",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Linked List exercise 💪\nImprove your TDD skills implementing LinkedList\n\n### Task\n`List` is an API that represents a well-known data structure. Your job is to \nimplement the *todo* section of the class `LinkedList` **following TDD rules**. Please note, that your implementation \nshould be based on **singly liked  nodes.** It means that you should create your own class `Node<T>` \nthat will hold list elements. \n  \nTo verify your implementation check if your `LinkedListTest.java` provides full **(100%) code coverage** and compare it \nwith **completed exercise**\n \n### Pre-conditions❗\nYou're supposed to know [The Three laws of TDD](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/6-0-test-driven-development#the-three-laws-of-tdd),\n be familiar with Linked List data structure, and be able to write Java code\n\n### How to start❓\n* Just clone the repository and create a branch **exercise/your_username** if you want your code to be reviewed\n* Start implementing the **todo** section and verify your changes by running tests\n* If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)\n* Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation\n \n### Related materials ℹ\n * [Linked Lists]() todo: add link on LinkedList materials\n * [Як виробити звичку писати тести? (Bobocode channel <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=20/>)](https://youtu.be/L_CiX9C51BI)\n * [The Three Laws of TDD](https://www.youtube.com/watch?v=qkblc5WRn-U&t=3476s)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>6-0-test-driven-development</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>6-1-2-linked-list</artifactId>\n\n\n</project>"
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/src/main/java/com/bobocode/tdd/LinkedList.java",
    "content": "package com.bobocode.tdd;\n\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link LinkedList} is a list implementation that is based on singly linked generic nodes. A node is implemented as\n * inner static class {@link Node<T>}.\n *\n * @param <T> generic type parameter\n */\npublic class LinkedList<T> implements List<T> {\n\n    /**\n     * This method creates a list of provided elements\n     *\n     * @param elements elements to add\n     * @param <T>      generic type\n     * @return a new list of elements the were passed as method parameters\n     */\n    public static <T> List<T> of(T... elements) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Adds an element to the end of the list.\n     *\n     * @param element element to add\n     */\n    @Override\n    public void add(T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Adds a new element to the specific position in the list. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index   an index of new element\n     * @param element element to add\n     */\n    @Override\n    public void add(int index, T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Changes the value of an list element at specific position. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index   an position of element to change\n     * @param element a new element value\n     */\n    @Override\n    public void set(int index, T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Retrieves an elements by its position index. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index element index\n     * @return an element value\n     */\n    @Override\n    public T get(int index) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the first element of the list. Operation is performed in constant time O(1)\n     *\n     * @return the first element of the list\n     * @throws java.util.NoSuchElementException if list is empty\n     */\n    @Override\n    public T getFirst() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the last element of the list. Operation is performed in constant time O(1)\n     *\n     * @return the last element of the list\n     * @throws java.util.NoSuchElementException if list is empty\n     */\n    @Override\n    public T getLast() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Removes an elements by its position index. In case provided index in out of the list bounds it\n     * throws {@link IndexOutOfBoundsException}\n     *\n     * @param index element index\n     * @return deleted element\n     */\n    @Override\n    public T remove(int index) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n\n    /**\n     * Checks if a specific exists in he list\n     *\n     * @return {@code true} if element exist, {@code false} otherwise\n     */\n    @Override\n    public boolean contains(T element) {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Checks if a list is empty\n     *\n     * @return {@code true} if list is empty, {@code false} otherwise\n     */\n    @Override\n    public boolean isEmpty() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Returns the number of elements in the list\n     *\n     * @return number of elements\n     */\n    @Override\n    public int size() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n\n    /**\n     * Removes all list elements\n     */\n    @Override\n    public void clear() {\n        throw new ExerciseNotCompletedException(); // todo: implement this method\n    }\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/src/main/java/com/bobocode/tdd/List.java",
    "content": "package com.bobocode.tdd;\n\n\npublic interface List<T> {\n    void add(T element);\n\n    void add(int index, T element);\n\n    void set(int index, T element);\n\n    T get(int index);\n\n    T getFirst();\n\n    T getLast();\n\n    T remove(int index);\n\n    boolean contains(T element);\n\n    boolean isEmpty();\n\n    int size();\n\n    void clear();\n\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/src/test/java/com/bobocode/tdd/LinkedListTest.java",
    "content": "package com.bobocode.tdd;\n\npublic class LinkedListTest {\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Binary Search Tree exercise :muscle:\nImprove your TDD skill implementing Binary Search Tree\n\n### Task\n**Binary Search Tree (BST)** is an ordered (sorted) data structure. Your job is to implement the interface `BinarySearchTree`\n by practicing TDD discipline\n \n### Pre-conditions :heavy_exclamation_mark:\nYou're supposed to know [The Three laws of TDD](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/6-0-test-driven-development#the-three-laws-of-tdd),\nbe familiar with [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree) data structure, \nand understand [Recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science))\n\n### How to start :question:\n* Just clone the repository and start implementing `BinarySearchTree` interface following *three laws of TDD*\n* If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)\n* Don't worry if you got stuck, checkout branch **completed** and see the final implementation\n \n### Related materials :information_source:\n * [Як виробити звичку писати тести? <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/yt_icon_rgb.png\" height=13/>](https://youtu.be/L_CiX9C51BI)\n * [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree)\n * [The Three Laws of TDD](https://www.youtube.com/watch?v=qkblc5WRn-U&t=3476s)\n\n---\n#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)\n\n##\n<div align=\"center\"><img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif\" height=50/></div>"
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>6-0-test-driven-development</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>6-1-3-binary-search-tree</artifactId>\n\n\n</project>"
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/BinarySearchTree.java",
    "content": "package com.bobocode.tdd;\n\nimport java.util.function.Consumer;\n\npublic interface BinarySearchTree<T extends Comparable> {\n    /**\n     * insert an element\n     * @return true if element did not exist in the tree and was inserted successfully\n     */\n    boolean insert(T element);\n\n    /**\n     * @return true if tree contains element\n     */\n    boolean contains(T element);\n\n    /**\n     * @return number of elements in the tree\n     */\n    int size();\n\n    /**\n     * @return max. number of transition between root node and any other node; 0 - if tree is empty or contains 1 element\n     */\n    int depth();\n\n    /**\n     * traverse the tree in element's natural order\n     * @param consumer accepts ref. to node during traversing\n     */\n    void inOrderTraversal(Consumer<T> consumer);\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java",
    "content": "package com.bobocode.tdd;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.function.Consumer;\n\npublic class RecursiveBinarySearchTree<T extends Comparable> implements BinarySearchTree<T> {\n\n    public static <T extends Comparable> RecursiveBinarySearchTree<T> of(T... elements) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public boolean insert(T element) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public boolean contains(T element) {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public int size() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public int depth() {\n        throw new ExerciseNotCompletedException();\n    }\n\n    @Override\n    public void inOrderTraversal(Consumer<T> consumer) {\n        throw new ExerciseNotCompletedException();\n    }\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/src/test/java/com/bobocode/tdd/BinarySearchTreeTest.java",
    "content": "package com.bobocode.tdd;\n\nclass BinarySearchTreeTest {\n\n}\n"
  },
  {
    "path": "6-0-test-driven-development/README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Test-Driven Development\nBuild strong TDD skills needed for enterprise Java development 💪\n\n##\n### The three laws of TDD\n1. You should not write production code until you have written a failing unit test\n2. You should not write more of a unit test than is sufficient to fail\n3. You should not write more production code than is sufficient to pass the failing test\n\n"
  },
  {
    "path": "6-0-test-driven-development/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>6-0-test-driven-development</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>6-1-1-stack</module>\n        <module>6-1-2-linked-list</module>\n        <module>6-1-3-binary-search-tree</module>\n    </modules>\n\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>java-fundamentals-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n            <scope>compile</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "CONTRIBUTING.MD",
    "content": "# Contributing to Java Fundamentals Course\n\nThank you for taking the time to contribute to the open-source education! 👏\n\nBefore moving forward, please **make sure that you understand what is an exercise and how this repository is organized.** The best place to learn about that is \n[Introduction module](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction) 🤓\n\n## Branching strategy\n\nIn this repository we maintain **two key branches** [main](https://github.com/bobocode-projects/java-fundamentals-exercises) and \n[completed](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/completed). Since the `main` branch stores the exercises, and the `completed` stores the \ncompleted solution, the latter is always ahead of the first one. Here's the rule:\n\n#### The [Completed Solution PR](https://github.com/bobocode-projects/java-fundamentals-exercises/pull/33) should always show only those changes that participats implement in the scope of the exercises❗️\n\nIt means two things:\n* any changes that are related to the exercise task, the tests, or the project configuration should be added first to the `main` branch and then merged from `main` to `completed`\n* only changes related to the completed solution should be added to the `completed` branch\n\n## Creating a Pull Request\n\n### PR with a small fix\n\nIf case you found a mistake and want to provide a fix, you can easily create a PR with corresponding changes. \nPlease make sure that you have read [the branching strategy](#branching-strategy) so your PR targets to the right place.\n\n### Double pull request\nIn case you need to **change both the exercise and the completed solution**, you will need to create **two pull requests.** In order to follow [the branching strategy](#branching-strategy),\nyou will need to do some **extra work with branches** ⚠️. \n\nImagine you need to add a new `methodX()` to the exercise. Here's the list of actions you will need to do:\n1. Create a local branch from `main` (let's call it `exercise branch`)\n2. Add `methodX()` that throws `ExerciseNotCompletedException`, and a corresponding `testMethodX()`\n3. Commit these changes to the `exercise branch`\n4. Push the changes\n5. Checkout `completed`\n6. Create a local branch from `completed` (let's call it `solution branch`)\n7. Merge your `exercise branch` into the `solution branch`\n8. Implement `methodX()` and make sure that tests pass\n9. Commit these changes to the `solution branch`\n10. Push the changes\n\n**If everything is fine, then you can create two pull requests:**\n\n11. Create a pull request from the `exercise branch` to the `main`\n12. Create a pull request from the `solution branch` to the `completed`\n\n### Branching naming convention \nA branch name should start from the ticket or issue number if one is available, following the short changes summary. Like this:\n\n* ```101-insane-lambdas-exercise```\n\nIn case the branch targets `completed` it should have a corresponding suffix. Like this:\n\n* ```101-insane-lambdas-exercise-completed```\n\n### Commits\n* In case you are working on the JIRA ticket, or the GitHub issue, please make sure that you always add its full number to every commit message\n* Please provide a descriptive messages\n* Use squash and rebase to avoid redundant commits\n\n### Authors\n* if you created a new exercise, please specify your name in the javadoc as @author\n* if you did a substantial change in the class or added new tests, please add you name to the list of authors\n\n\n## Submitting an issue\nIn case you have faced **an issue that you couldn't resolve** and/or you have **a good idea how this repository can be improved**, feel free to [submit an issue](https://github.com/bobocode-projects/java-fundamentals-exercises/issues/new).\n\nPlease note that **if you just have a question, you can ask it in one of the three different ways** depending on the quesiton:\n* add you comment to the [Completed PR](https://github.com/bobocode-projects/java-fundamentals-exercises/pull/33#issue-790887000) – if you want to ask about the completed solution\n* [start a new discussion](https://github.com/bobocode-projects/java-fundamentals-exercises/discussions/new) under Q&A – if you have question related to this repo\n* contact us via info@bobocode.com – if you have other questions\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" height=50/>Welcome to the Java Fundamentals Exercises\nBuild strong fundamental skills that you will need for real-world Enterprise Java development\n\n## Why\nMost people don’t know how to learn Enterprise Java efficiently. So we create an **Open-source Java Education**\nthat helps them to **master strong skills**, learn **world best practices** and build a **successful career**. 🚀\n\nAt Bobocode we have extensive experience in both building Enterprise Java applications and organizing efficient learning.\nTherefore, this course covers what you need in the most efficient way. We believe that\n**the key to efficient learning is practice**. 💪 And as a software engineer, you should **spend as much time as you can in the IDE writing code**.\nAt the end of the day, this is the only place where you build software... 💻\n\n## About this repo\nJava Standard Edition is huge. Computer Science is huge. Object-Oriented programming, as well as Functional programming, \nare also pretty big topics. So how can you learn everything you need and **don't get stuck for years learning fundamentals only?** 🤔 \nYou're in the right place to find the answer. 😀\n\nThis repo gives you two uniques features that will help you to master fundamentals ASAP:\n1. It consists of **selected topics that are must-have** for real-world enterprise Java development ⭐️\n2. It is fully based on **special training exercises** that put your practice on rails and **boost up your learning efficiency** 🚀\n\nGo ahead and check out [Introduction module](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction) 👍\n"
  },
  {
    "path": "java-fundamentals-util/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>java-fundamentals-util</artifactId>\n    \n    <dependencies>\n        <dependency>\n            <groupId>com.devskiller</groupId>\n            <artifactId>jfairy</artifactId>\n            <version>0.6.5</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/data/Accounts.java",
    "content": "package com.bobocode.data;\n\nimport com.bobocode.model.Account;\nimport com.bobocode.model.CreditAccount;\nimport com.bobocode.model.Sex;\nimport com.devskiller.jfairy.Fairy;\nimport com.devskiller.jfairy.producer.person.Person;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.stream.Stream;\n\nimport static java.util.stream.Collectors.toList;\n\npublic class Accounts {\n    public static int MAX_BALANCE_VALUE = 200_000;\n\n    public static Account generateAccount() {\n        Person person = generatePerson();\n        Account account = convertToAccount(person);\n        fillCommonRandomFields(account);\n        return account;\n    }\n\n    public static CreditAccount generateCreditAccount() {\n        Person person = generatePerson();\n        CreditAccount account = convertToCreditAccount(person);\n        fillCommonRandomFields(account);\n        account.setCreditBalance(randomBigDecimal(MAX_BALANCE_VALUE));\n        return account;\n    }\n\n    public static List<Account> generateAccountList(int size) {\n        return Stream.generate(Accounts::generateAccount)\n                .limit(size)\n                .collect(toList());\n    }\n\n    public static List<CreditAccount> generateCreditAccountList(int size) {\n        return Stream.generate(Accounts::generateCreditAccount)\n                .limit(size)\n                .collect(toList());\n    }\n\n    private static Person generatePerson() {\n        Fairy fairy = Fairy.create();\n        return fairy.person();\n    }\n\n    private static Account convertToAccount(Person person) {\n        Account account = new Account();\n        fillAccount(account, person);\n        return account;\n    }\n\n    private static CreditAccount convertToCreditAccount(Person person) {\n        CreditAccount account = new CreditAccount();\n        fillAccount(account, person);\n        return account;\n    }\n\n    private static void fillAccount(Account account, Person person) {\n        account.setFirstName(person.getFirstName());\n        account.setLastName(person.getLastName());\n        account.setEmail(person.getEmail());\n        account.setBirthday(LocalDate.of(\n                person.getDateOfBirth().getYear(),\n                person.getDateOfBirth().getMonth(),\n                person.getDateOfBirth().getDayOfMonth()));\n        account.setSex(Sex.valueOf(person.getSex().name()));\n    }\n\n    private static void fillCommonRandomFields(Account account) {\n        BigDecimal balance = randomBigDecimal(MAX_BALANCE_VALUE);\n        account.setBalance(balance);\n        account.setCreationDate(LocalDate.now());\n    }\n\n    private static BigDecimal randomBigDecimal(int max) {\n        return BigDecimal.valueOf(ThreadLocalRandom.current().nextInt(max));\n    }\n}\n\n"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/model/Account.java",
    "content": "package com.bobocode.model;\n\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\n\n@NoArgsConstructor\n@AllArgsConstructor(access = AccessLevel.PUBLIC)\n@Getter\n@Setter\n@ToString\n@EqualsAndHashCode(of = \"email\")\npublic class Account {\n    private Long id;\n    private String firstName;\n    private String lastName;\n    private String email;\n    private LocalDate birthday;\n    private Sex sex;\n    private LocalDate creationDate;\n    private BigDecimal balance = BigDecimal.ZERO;\n}\n\n"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/model/CreditAccount.java",
    "content": "package com.bobocode.model;\n\nimport lombok.AccessLevel;\nimport lombok.AllArgsConstructor;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\n\nimport java.math.BigDecimal;\nimport java.util.Optional;\n\n@NoArgsConstructor\n@AllArgsConstructor(access = AccessLevel.PUBLIC)\n@Setter\npublic class CreditAccount extends Account {\n    private BigDecimal creditBalance;\n\n    public Optional<BigDecimal> getCreditBalance() {\n        return Optional.ofNullable(creditBalance);\n    }\n}\n"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/model/Sex.java",
    "content": "package com.bobocode.model;\n\npublic enum Sex {\n    MALE,\n    FEMALE\n}\n"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/util/ExerciseNotCompletedException.java",
    "content": "package com.bobocode.util;\n\n/**\n * This is a custom exception that we throw in every method which should be implemented as a part of the exercise.\n * If you see that it was thrown it means that you did not implement all required methods yet.\n */\npublic class ExerciseNotCompletedException extends RuntimeException {\n    public ExerciseNotCompletedException() {\n        super(\"Implement this method and remove exception OR switch to branch completed if you got stuck.\");\n    }\n}\n"
  },
  {
    "path": "lesson-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>java-fundamentals-exercises</artifactId>\n        <groupId>com.bobocode</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lesson-demo</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.bobocode</groupId>\n            <artifactId>java-fundamentals-util</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "lesson-demo/src/main/java/com/bobocode/DemoApp.java",
    "content": "package com.bobocode;\n\npublic class DemoApp {\n    public static void main(String[] args) {\n    }\n}\n"
  },
  {
    "path": "mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n      PRG=\"$link\"\n    else\n      PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Mingw, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -n \"$JAVA_HOME\"  ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"`which java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\n##########################################################################################\n# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n# This allows using the maven wrapper in projects that prohibit checking in binary data.\n##########################################################################################\nif [ -r \"$BASE_DIR/.mvn/wrapper/maven-wrapper.jar\" ]; then\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Found .mvn/wrapper/maven-wrapper.jar\"\n    fi\nelse\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ...\"\n    fi\n    if [ -n \"$MVNW_REPOURL\" ]; then\n      jarUrl=\"$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar\"\n    else\n      jarUrl=\"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar\"\n    fi\n    while IFS=\"=\" read key value; do\n      case \"$key\" in (wrapperUrl) jarUrl=\"$value\"; break ;;\n      esac\n    done < \"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties\"\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Downloading from: $jarUrl\"\n    fi\n    wrapperJarPath=\"$BASE_DIR/.mvn/wrapper/maven-wrapper.jar\"\n    if $cygwin; then\n      wrapperJarPath=`cygpath --path --windows \"$wrapperJarPath\"`\n    fi\n\n    if command -v wget > /dev/null; then\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Found wget ... using wget\"\n        fi\n        if [ -z \"$MVNW_USERNAME\" ] || [ -z \"$MVNW_PASSWORD\" ]; then\n            wget \"$jarUrl\" -O \"$wrapperJarPath\"\n        else\n            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD \"$jarUrl\" -O \"$wrapperJarPath\"\n        fi\n    elif command -v curl > /dev/null; then\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Found curl ... using curl\"\n        fi\n        if [ -z \"$MVNW_USERNAME\" ] || [ -z \"$MVNW_PASSWORD\" ]; then\n            curl -o \"$wrapperJarPath\" \"$jarUrl\" -f\n        else\n            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o \"$wrapperJarPath\" \"$jarUrl\" -f\n        fi\n\n    else\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Falling back to using Java to download\"\n        fi\n        javaClass=\"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java\"\n        # For Cygwin, switch paths to Windows format before running javac\n        if $cygwin; then\n          javaClass=`cygpath --path --windows \"$javaClass\"`\n        fi\n        if [ -e \"$javaClass\" ]; then\n            if [ ! -e \"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class\" ]; then\n                if [ \"$MVNW_VERBOSE\" = true ]; then\n                  echo \" - Compiling MavenWrapperDownloader.java ...\"\n                fi\n                # Compiling the Java class\n                (\"$JAVA_HOME/bin/javac\" \"$javaClass\")\n            fi\n            if [ -e \"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class\" ]; then\n                # Running the downloader\n                if [ \"$MVNW_VERBOSE\" = true ]; then\n                  echo \" - Running MavenWrapperDownloader.java ...\"\n                fi\n                (\"$JAVA_HOME/bin/java\" -cp .mvn/wrapper MavenWrapperDownloader \"$MAVEN_PROJECTBASEDIR\")\n            fi\n        fi\n    fi\nfi\n##########################################################################################\n# End of extension\n##########################################################################################\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\nif [ \"$MVNW_VERBOSE\" = true ]; then\n  echo $MAVEN_PROJECTBASEDIR\nfi\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\n# Provide a \"standardized\" way to retrieve the CLI args that will\n# work with both Windows and non-Windows executions.\nMAVEN_CMD_LINE_ARGS=\"$MAVEN_CONFIG $@\"\nexport MAVEN_CMD_LINE_ARGS\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Maven Start Up Batch script\n@REM\n@REM Required ENV vars:\n@REM JAVA_HOME - location of a JDK home dir\n@REM\n@REM Optional ENV vars\n@REM M2_HOME - location of maven2's installed home dir\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\n@REM     e.g. to debug Maven itself, use\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n@REM ----------------------------------------------------------------------------\n\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\n@echo off\n@REM set title of command window\ntitle %0\n@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\n\n@REM set %HOME% to equivalent of $HOME\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\n\n@REM Execute a user defined script before this one\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_pre.bat\" call \"%HOME%\\mavenrc_pre.bat\"\nif exist \"%HOME%\\mavenrc_pre.cmd\" call \"%HOME%\\mavenrc_pre.cmd\"\n:skipRcPre\n\n@setlocal\n\nset ERROR_CODE=0\n\n@REM To isolate internal variables from possible post scripts, we use another setlocal\n@setlocal\n\n@REM ==== START VALIDATION ====\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\n\necho.\necho Error: JAVA_HOME not found in your environment. >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n:OkJHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\n\necho.\necho Error: JAVA_HOME is set to an invalid directory. >&2\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n@REM ==== END VALIDATION ====\n\n:init\n\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\n@REM Fallback to current working directory if not found.\n\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\n\nset EXEC_DIR=%CD%\nset WDIR=%EXEC_DIR%\n:findBaseDir\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\ncd ..\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\nset WDIR=%CD%\ngoto findBaseDir\n\n:baseDirFound\nset MAVEN_PROJECTBASEDIR=%WDIR%\ncd \"%EXEC_DIR%\"\ngoto endDetectBaseDir\n\n:baseDirNotFound\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\ncd \"%EXEC_DIR%\"\n\n:endDetectBaseDir\n\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\n\n@setlocal EnableExtensions EnableDelayedExpansion\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\n\n:endReadAdditionalConfig\n\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nset DOWNLOAD_URL=\"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar\"\n\nFOR /F \"tokens=1,2 delims==\" %%A IN (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.properties\") DO (\n    IF \"%%A\"==\"wrapperUrl\" SET DOWNLOAD_URL=%%B\n)\n\n@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n@REM This allows using the maven wrapper in projects that prohibit checking in binary data.\nif exist %WRAPPER_JAR% (\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Found %WRAPPER_JAR%\n    )\n) else (\n    if not \"%MVNW_REPOURL%\" == \"\" (\n        SET DOWNLOAD_URL=\"%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar\"\n    )\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Couldn't find %WRAPPER_JAR%, downloading it ...\n        echo Downloading from: %DOWNLOAD_URL%\n    )\n\n    powershell -Command \"&{\"^\n\t\t\"$webclient = new-object System.Net.WebClient;\"^\n\t\t\"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {\"^\n\t\t\"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');\"^\n\t\t\"}\"^\n\t\t\"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')\"^\n\t\t\"}\"\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Finished downloading %WRAPPER_JAR%\n    )\n)\n@REM End of extension\n\n@REM Provide a \"standardized\" way to retrieve the CLI args that will\n@REM work with both Windows and non-Windows executions.\nset MAVEN_CMD_LINE_ARGS=%*\n\n%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\nif ERRORLEVEL 1 goto error\ngoto end\n\n:error\nset ERROR_CODE=1\n\n:end\n@endlocal & set ERROR_CODE=%ERROR_CODE%\n\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPost\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_post.bat\" call \"%HOME%\\mavenrc_post.bat\"\nif exist \"%HOME%\\mavenrc_post.cmd\" call \"%HOME%\\mavenrc_post.cmd\"\n:skipRcPost\n\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\nif \"%MAVEN_BATCH_PAUSE%\" == \"on\" pause\n\nif \"%MAVEN_TERMINATE_CMD%\" == \"on\" exit %ERROR_CODE%\n\nexit /B %ERROR_CODE%\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.bobocode</groupId>\n    <artifactId>java-fundamentals-exercises</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>pom</packaging>\n\n    <properties>\n        <maven.compiler.source>21</maven.compiler.source>\n        <maven.compiler.target>21</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <modules>\n        <module>0-0-intro</module>\n        <module>1-0-java-basics</module>\n        <module>2-0-data-structures-and-algorithms</module>\n        <module>3-0-java-core</module>\n        <module>4-0-object-oriented-programming</module>\n        <module>5-0-functional-programming</module>\n        <module>6-0-test-driven-development</module>\n        <module>java-fundamentals-util</module>\n        <module>lesson-demo</module>\n    </modules>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-engine</artifactId>\n            <version>5.10.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-params</artifactId>\n            <version>5.10.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.24.2</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.30</version>\n        </dependency>\n        <dependency>\n            <groupId>com.google.code.findbugs</groupId>\n            <artifactId>jsr305</artifactId>\n            <version>3.0.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-core</artifactId>\n            <version>5.6.0</version>\n            <scope>test</scope>\n            <exclusions>\n                <exclusion>\n                    <groupId>net.bytebuddy</groupId>\n                    <artifactId>byte-buddy</artifactId>\n                </exclusion>\n                <exclusion>\n                    <groupId>net.bytebuddy</groupId>\n                    <artifactId>byte-buddy-agent</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>net.bytebuddy</groupId>\n            <artifactId>byte-buddy</artifactId>\n            <version>1.14.9</version>\n        </dependency>\n        <dependency>\n            <groupId>net.bytebuddy</groupId>\n            <artifactId>byte-buddy-agent</artifactId>\n            <version>1.14.9</version>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <version>5.2.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-simple</artifactId>\n            <version>2.0.9</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>3.11.0</version>\n                    <configuration>\n                        <compilerArgs>\n                            <arg>-parameters</arg>\n                        </compilerArgs>\n                    </configuration>\n                </plugin>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-surefire-plugin</artifactId>\n                    <version>3.1.2</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n</project>"
  }
]