main 707802e9af36 cached
201 files
503.6 KB
130.9k tokens
1067 symbols
1 requests
Download .txt
Showing preview only (578K chars total). Download the full file or copy to clipboard to get everything.
Repository: bobocode-projects/java-fundamentals-exercises
Branch: main
Commit: 707802e9af36
Files: 201
Total size: 503.6 KB

Directory structure:
gitextract_r1nqldzq/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── maven.yml
│       └── projectActions.yml
├── .gitignore
├── .mvn/
│   └── wrapper/
│       ├── MavenWrapperDownloader.java
│       ├── maven-wrapper.jar
│       └── maven-wrapper.properties
├── 0-0-intro/
│   ├── README.md
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── com/
│       │           └── bobocode/
│       │               └── intro/
│       │                   └── ExerciseIntroduction.java
│       └── test/
│           └── java/
│               └── com/
│                   └── bobocode/
│                       └── intro/
│                           └── ExerciseIntroductionTest.java
├── 1-0-java-basics/
│   ├── 1-3-0-hello-generics/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── basics/
│   │       │                   ├── Box.java
│   │       │                   └── BoxDemoApp.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── basics/
│   │                           └── BoxTest.java
│   ├── 1-3-1-crazy-generics/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── basics/
│   │       │                   ├── CrazyGenerics.java
│   │       │                   └── util/
│   │       │                       └── BaseEntity.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── basics/
│   │                           └── CrazyGenericsTest.java
│   ├── 1-3-2-heterogeneous-max-holder/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── basics/
│   │       │                   └── HeterogeneousMaxHolder.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── basics/
│   │                           └── HeterogeneousMaxHolderTest.java
│   ├── 1-5-0-hello-annotations/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── basics/
│   │       │                   ├── HelloAnnotationsExercise.java
│   │       │                   └── Level.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── basics/
│   │                           └── HelloAnnotationsExerciseTest.java
│   ├── README.md
│   └── pom.xml
├── 2-0-data-structures-and-algorithms/
│   ├── 2-2-1-node/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobobode/
│   │       │               └── cs/
│   │       │                   ├── Node.java
│   │       │                   └── Nodes.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── NodesTest.java
│   ├── 2-2-2-stack/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   ├── LinkedStack.java
│   │       │                   ├── Stack.java
│   │       │                   └── exception/
│   │       │                       └── EmptyStackException.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── LinkedStackTest.java
│   ├── 2-2-3-linked-queue/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   ├── LinkedQueue.java
│   │       │                   └── Queue.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── LinkedQueueTest.java
│   ├── 2-2-4-linked-list/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   └── LinkedList.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── LinkedListTest.java
│   ├── 2-2-5-array-list/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   └── ArrayList.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── ArrayListTest.java
│   ├── 2-2-6-binary-search-tree/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   ├── BinarySearchTree.java
│   │       │                   └── RecursiveBinarySearchTree.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── RecursiveBinarySearchTreeTest.java
│   ├── 2-2-9-hash-table/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   ├── HashTable.java
│   │       │                   └── Map.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── HashTableTest.java
│   ├── README.md
│   ├── data-structures-and-algorithms-util/
│   │   ├── pom.xml
│   │   └── src/
│   │       └── main/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── List.java
│   └── pom.xml
├── 3-0-java-core/
│   ├── 3-6-1-file-reader/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── se/
│   │       │                   └── FileReaders.java
│   │       └── test/
│   │           ├── java/
│   │           │   └── com/
│   │           │       └── bobocode/
│   │           │           └── se/
│   │           │               └── FileReadersTest.java
│   │           └── resources/
│   │               ├── empty.txt
│   │               ├── lines.txt
│   │               └── simple.txt
│   ├── 3-6-2-file-stats/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── se/
│   │       │                   ├── FileStats.java
│   │       │                   └── FileStatsException.java
│   │       └── test/
│   │           ├── java/
│   │           │   └── com/
│   │           │       └── bobocode/
│   │           │           └── se/
│   │           │               └── FileStatsTest.java
│   │           └── resources/
│   │               ├── scosb.txt
│   │               └── sotl.txt
│   ├── 3-6-3-crazy-regex/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── se/
│   │       │                   └── CrazyRegex.java
│   │       └── test/
│   │           ├── java/
│   │           │   └── com/
│   │           │       └── bobocode/
│   │           │           └── se/
│   │           │               └── CrazyRegexTest.java
│   │           └── resources/
│   │               ├── nasa.json
│   │               └── note.txt
│   ├── 3-6-4-random-field-comparator/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── se/
│   │       │                   └── RandomFieldComparator.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── se/
│   │                           └── RandomFieldComparatorTest.java
│   ├── README.md
│   └── pom.xml
├── 4-0-object-oriented-programming/
│   ├── 4-3-1-flight-search/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── oop/
│   │       │                   ├── data/
│   │       │                   │   └── FlightDao.java
│   │       │                   ├── factory/
│   │       │                   │   └── FlightServiceFactory.java
│   │       │                   └── service/
│   │       │                       └── FlightService.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── oop/
│   │                           └── FlightServiceTest.java
│   ├── README.md
│   └── pom.xml
├── 5-0-functional-programming/
│   ├── 5-0-1-lambda-functions-map/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── FunctionMap.java
│   │       │                   ├── Functions.java
│   │       │                   └── exception/
│   │       │                       └── InvalidFunctionNameException.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── FunctionsTest.java
│   ├── 5-0-2-stream-sum-of-squares/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── SumOfSquares.java
│   │       │                   └── exception/
│   │       │                       └── InvalidRangeException.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── SumOfSquaresTest.java
│   ├── 5-1-1-crazy-lambdas/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   └── CrazyLambdas.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── CrazyLambdasTest.java
│   ├── 5-2-1-crazy-streams/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── CrazyStreams.java
│   │       │                   └── exception/
│   │       │                       └── EntityNotFoundException.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── CrazyStreamsTest.java
│   ├── 5-3-1-crazy-optionals/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── CrazyOptionals.java
│   │       │                   ├── exception/
│   │       │                   │   └── AccountNotFoundException.java
│   │       │                   └── function/
│   │       │                       ├── AccountProvider.java
│   │       │                       ├── AccountService.java
│   │       │                       └── CreditAccountProvider.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── CrazyOptionalsTest.java
│   ├── 5-4-1-fun-prime-numbers/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── OOSumOfPrimes.java
│   │       │                   └── PrimeNumbers.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── PrimeNumbersTest.java
│   ├── README.md
│   ├── pom.xml
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── bobocode/
│                       ├── lambdas/
│                       │   └── tutorial/
│                       │       ├── CrazyLambdaExample.java
│                       │       ├── FunctionComposition.java
│                       │       ├── LambdaAndMethodReference.java
│                       │       ├── LambdaComparatorExample.java
│                       │       ├── LambdaRunnableExample.java
│                       │       ├── MethodRefToUpperCaseExample.java
│                       │       ├── MethodReferenceExamples.java
│                       │       ├── PredefinedInterfacePrimitives.java
│                       │       └── PredefinedInterfacesExamples.java
│                       ├── optionals/
│                       │   └── tutorial/
│                       │       ├── OptionalCreation.java
│                       │       ├── OptionalExamples.java
│                       │       ├── OptionalImperativeVsDeclarativeCheck.java
│                       │       ├── OptionalMapping.java
│                       │       ├── OptionalPrimitives.java
│                       │       ├── OptionalReturningMethod.java
│                       │       ├── OptionalSearchByEmailExample.java
│                       │       ├── OptionalStream.java
│                       │       └── README.MD
│                       └── streams/
│                           └── tutorial/
│                               ├── ImperativeVsDeclarativeFiltering.java
│                               ├── ImperativeVsDeclarativeMax.java
│                               ├── README.MD
│                               ├── StreamAdditionalFeatures.java
│                               ├── StreamBasics.java
│                               ├── StreamCollecting.java
│                               ├── StreamFiltering.java
│                               ├── StreamMapping.java
│                               ├── StreamParallelProcessing.java
│                               ├── StreamPrimitives.java
│                               ├── StreamReducing.java
│                               ├── StreamSideEffectFilteringExample.java
│                               └── StreamWhileExample.java
├── 6-0-test-driven-development/
│   ├── 6-1-1-stack/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── tdd/
│   │       │                   ├── LinkedStack.java
│   │       │                   └── Stack.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── tdd/
│   │                           └── StackTest.java
│   ├── 6-1-2-linked-list/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── tdd/
│   │       │                   ├── LinkedList.java
│   │       │                   └── List.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── tdd/
│   │                           └── LinkedListTest.java
│   ├── 6-1-3-binary-search-tree/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── tdd/
│   │       │                   ├── BinarySearchTree.java
│   │       │                   └── RecursiveBinarySearchTree.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── tdd/
│   │                           └── BinarySearchTreeTest.java
│   ├── README.md
│   └── pom.xml
├── CONTRIBUTING.MD
├── LICENSE
├── README.md
├── java-fundamentals-util/
│   ├── pom.xml
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── bobocode/
│                       ├── data/
│                       │   └── Accounts.java
│                       ├── model/
│                       │   ├── Account.java
│                       │   ├── CreditAccount.java
│                       │   └── Sex.java
│                       └── util/
│                           └── ExerciseNotCompletedException.java
├── lesson-demo/
│   ├── pom.xml
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── bobocode/
│                       └── DemoApp.java
├── mvnw
├── mvnw.cmd
└── pom.xml

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

patreon: bobocode


================================================
FILE: .github/workflows/maven.yml
================================================
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven

name: Build completed branch with tests

on:
  push:
    branches: [ completed ]
  pull_request:
    branches: [ completed ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 17
      uses: actions/setup-java@v1
      with:
        java-version: 17
    - name: Build with Maven
      run: mvn -B package --file pom.xml


================================================
FILE: .github/workflows/projectActions.yml
================================================
name: Auto Assign to Project(s)

on:
  issues:
    types: [opened, labeled]
  pull_request:
    types: [opened, labeled]
env:
  GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }}

jobs:
  assign_one_project:
    runs-on: ubuntu-latest
    name: Assign to One Project
    steps:
      - name: Assign NEW issues project 4
        uses: srggrs/assign-one-project-github-action@1.2.1
        if: github.event.action == 'opened' || contains(github.event.issue.labels.*.name, 'java-fundamentals')
        with:
          project: 'https://github.com/bobocode-projects/java-fundamentals-exercises/projects/4'


================================================
FILE: .gitignore
================================================
.idea
**/*.iml
**/target


================================================
FILE: .mvn/wrapper/MavenWrapperDownloader.java
================================================
/*
 * Copyright 2007-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;

public class MavenWrapperDownloader {

    private static final String WRAPPER_VERSION = "0.5.6";
    /**
     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
     */
    private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
        + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";

    /**
     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
     * use instead of the default one.
     */
    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
            ".mvn/wrapper/maven-wrapper.properties";

    /**
     * Path where the maven-wrapper.jar will be saved to.
     */
    private static final String MAVEN_WRAPPER_JAR_PATH =
            ".mvn/wrapper/maven-wrapper.jar";

    /**
     * Name of the property which should be used to override the default download url for the wrapper.
     */
    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";

    public static void main(String args[]) {
        System.out.println("- Downloader started");
        File baseDirectory = new File(args[0]);
        System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());

        // If the maven-wrapper.properties exists, read it and check if it contains a custom
        // wrapperUrl parameter.
        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
        String url = DEFAULT_DOWNLOAD_URL;
        if(mavenWrapperPropertyFile.exists()) {
            FileInputStream mavenWrapperPropertyFileInputStream = null;
            try {
                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
                Properties mavenWrapperProperties = new Properties();
                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
            } catch (IOException e) {
                System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
            } finally {
                try {
                    if(mavenWrapperPropertyFileInputStream != null) {
                        mavenWrapperPropertyFileInputStream.close();
                    }
                } catch (IOException e) {
                    // Ignore ...
                }
            }
        }
        System.out.println("- Downloading from: " + url);

        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
        if(!outputFile.getParentFile().exists()) {
            if(!outputFile.getParentFile().mkdirs()) {
                System.out.println(
                        "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
            }
        }
        System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
        try {
            downloadFileFromURL(url, outputFile);
            System.out.println("Done");
            System.exit(0);
        } catch (Throwable e) {
            System.out.println("- Error downloading");
            e.printStackTrace();
            System.exit(1);
        }
    }

    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
        if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
            String username = System.getenv("MVNW_USERNAME");
            char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
            Authenticator.setDefault(new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });
        }
        URL website = new URL(urlString);
        ReadableByteChannel rbc;
        rbc = Channels.newChannel(website.openStream());
        FileOutputStream fos = new FileOutputStream(destination);
        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        fos.close();
        rbc.close();
    }

}


================================================
FILE: .mvn/wrapper/maven-wrapper.properties
================================================
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar


================================================
FILE: 0-0-intro/README.md
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Introduction

Learn how to use this course to build strong skill needed for enterprise Java development 

## The only way to learn effectively is to **learn by doing!** 💪

Therefore, 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.

Each exercise has three major parts:
* **a task** – some logic that you implement in order build a certain skill 👨🏻‍💻
* **a test** – a corresponding test that verifies if you implement the task correctly ▶️
* **a completed solution** - a branch `completed` that holds implemented task ✅

Go ahead and do this exercise by implementing a method in `Introduction` class. 💪 
_(we know it's silly, but we wanted to give you a simple example 😀)_

## You should have installed on your local machine ❗️
* [JDK 11+](https://jdk.java.net/15/)
* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)

## How to start ❓
* [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
* **open** the project via IDE

## Have questions? 🧐
* take a look at **README** of the exercise
* switch to branch `completed` and **see the correct implementation** of the exercise
* [join the discussion](https://github.com/bobocode-projects/java-fundamentals-exercises/discussions) on the GitHub
* contact us via info@bobocode.com


================================================
FILE: 0-0-intro/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.bobocode</groupId>
        <artifactId>java-fundamentals-exercises</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>0-0-intro</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.bobocode</groupId>
            <artifactId>java-fundamentals-util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

================================================
FILE: 0-0-intro/src/main/java/com/bobocode/intro/ExerciseIntroduction.java
================================================
package com.bobocode.intro;

import com.bobocode.util.ExerciseNotCompletedException;

/**
 * Welcome! This is an introduction exercise that will show you a simple example of Bobocode exercises.
 * <p>
 * JavaDoc is a way of communication with other devs. We use Java Docs in every exercise to define the task.
 * So PLEASE MAKE SURE you read the Java Docs carefully.
 * <p>
 * Every exercise is covered with tests, see {@link ExerciseIntroductionTest}.
 * <p>
 * In this repo you'll find dozens of exercises covering various fundamental topics.
 * They all have the same structure helping you to focus on practice and build strong skills!
 *
 * @author Taras Boychuk
 */
public class ExerciseIntroduction {
    /**
     * This method returns a very important message. If understood well, it can save you years of inefficient learning,
     * and unlock your potential!
     *
     * @return "The key to efficient learning is practice!"
     */
    public String getWelcomeMessage() {
        // todo: implement a method and return a message according to javadoc
        throw new ExerciseNotCompletedException(); 
    }

    /**
     * Method encodeMessage accepts one {@link String} parameter and returns encoded {@link String}.
     * <p>
     * PLEASE NOTE THAT YOU WILL GET STUCK ON THIS METHOD INTENTIONALLY! ;)
     * <p>
     * Every exercise has a completed solution that is stored in the branch "completed". So in case you got stuck
     * and don't know what to do, go check out completed solution.
     *
     * @param message input message
     * @return encoded message
     */
    public String encodeMessage(String message) {
        // todo: switch to branch "completed" in order to see how it should be implemented
        throw new ExerciseNotCompletedException();
    }
}


================================================
FILE: 0-0-intro/src/test/java/com/bobocode/intro/ExerciseIntroductionTest.java
================================================
package com.bobocode.intro;

import lombok.SneakyThrows;
import org.junit.jupiter.api.*;

import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * This is a {@link ExerciseIntroductionTest} that is meant to verify if you properly implement {@link ExerciseIntroduction}. 
 * It is a simple example that shows how each exercise is organized: todo section + tests.
 * <p>
 * A typical Java test uses JUnit framework to run the test, and may also use some other frameworks for assertions.
 * In our exercises we use JUnit 5 + AssertJ
 * <p>
 * PLEASE NOTE:
 * - annotation @{@link Order} is used to help you to understand which method should be implemented first.
 * - annotation @{@link DisplayName} is used to provide you more detailed instructions.
 *
 * @author Taras Boychuk
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ExerciseIntroductionTest {
    private ExerciseIntroduction exerciseIntroduction = new ExerciseIntroduction();
    private String EXPECTED_MESSAGE = "The key to efficient learning is practice!";

    @Test
    @Order(1)
    @DisplayName("getWelcomeMessage method returns correct phrase")
    void getWelcomeMessage() {
        String message = exerciseIntroduction.getWelcomeMessage();

        assertThat(message).isEqualTo(EXPECTED_MESSAGE);
    }

    @Test
    @Order(2)
    @DisplayName("encodeMessage returns correct encoded message")
    @SneakyThrows
    void encodeMessageReturnsCorrectPhrase() {
        var encodeMessageMethod = Arrays.stream(ExerciseIntroduction.class.getDeclaredMethods())
                .filter(method -> method.getName().equals("encodeMessage"))
                .findAny()
                .orElseThrow();

        var encodedMessage = encodeMessageMethod.invoke(new ExerciseIntroduction(), EXPECTED_MESSAGE);

        assertThat(encodedMessage).isEqualTo("VGhlIGtleSB0byBlZmZpY2llbnQgbGVhcm5pbmcgaXMgcHJhY3RpY2Uh");
    }
}


================================================
FILE: 1-0-java-basics/1-3-0-hello-generics/README.MD
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Hello Generics :muscle:

Learn how to do **safe type parametrization** using generic types 💪

### Pre-conditions ❗

You're supposed to be familiar **Java classes** and **casting**

### Objectives

* refactor class `Box` to introduce a generic type ✅
* change the value field type to parametrized (generic) type ✅
* change the **constructor parameter type** to parametrized type ✅
* change the **setter parameter type** to parametrized type ✅
* change the **getter return type** to parametrized type ✅
* refactor `BoxDemoApp` to specify generic type as `Integer` and receive **compile time error** ✅

##

In many cases the logic we implement does not depend on the data type. In other words, **many algorithms are the same
regardless if you apply them to Integer, String or any other types.** So in order to leverage the same logic for different 
types we need some form of type parametrization. Of course, we can use `Object` and store anything we want, but
it is not safe. Using `Object` requires runtime casting which can cause runtime exceptions.

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>


================================================
FILE: 1-0-java-basics/1-3-0-hello-generics/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>1-0-java-basics</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>1-3-0-hello-generics</artifactId>

</project>

================================================
FILE: 1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/Box.java
================================================
package com.bobocode.basics;

/**
 * {@link Box} is a container class that can store a value of any given type. Using Object as a field type
 * is flexible, because we can store anything we want there. But it is not safe, because it requires runtime casting
 * and there is no guarantee that we know the type of the stored value.
 * <p>
 * todo: refactor this class so it uses generic type "T" and run {@link com.bobocode.basics.BoxTest} to verify it
 */
public class Box {
    private Object value;

    public Box(Object value) {
        this.value = value;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }
}


================================================
FILE: 1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/BoxDemoApp.java
================================================
package com.bobocode.basics;

/**
 * This demo demonstrates why using Object is not safe. It's not safe because runtime casting can cause runtime
 * exceptions. We should always fail as soon as possible. So in this code we should not allow setting String
 * value at compile time, if we expect to work with integers.
 * <p>
 * todo: refactor class {@link Box} to make type parameterization safe and make this demo fail at compile time
 */
public class BoxDemoApp {
    public static void main(String[] args) {
        Box intBox = new Box(123);
        Box intBox2 = new Box(321);
        System.out.println((int) intBox.getValue() + (int) intBox2.getValue());

        intBox.setValue(222);
        intBox.setValue("abc"); // this should not be allowed
        // the following code will compile, but will throw runtime exception
        System.out.println((int) intBox.getValue() + (int) intBox2.getValue());
    }
}


================================================
FILE: 1-0-java-basics/1-3-0-hello-generics/src/test/java/com/bobocode/basics/BoxTest.java
================================================
package com.bobocode.basics;

import lombok.SneakyThrows;
import org.junit.jupiter.api.*;

import static org.assertj.core.api.Assertions.assertThat;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BoxTest {
    private final String TYPE_PARAMETER_NAME = "T";

    @Test
    @Order(1)
    @DisplayName("Box class has one type parameter")
    void boxClassHasOneTypeParameter() {
        var typeParameters = Box.class.getTypeParameters();

        assertThat(typeParameters.length).isEqualTo(1);
    }

    @Test
    @Order(2)
    @DisplayName("Type parameter is called \"T\"")
    void typeParameterIsCalledT() {
        var typeParameter = Box.class.getTypeParameters()[0];

        assertThat(typeParameter.getName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(3)
    @DisplayName("Type parameter \"T\" is not bounded")
    void typeParameterIsNotBounded() {
        var typeParameter = Box.class.getTypeParameters()[0];

        assertThat(typeParameter.getBounds()).hasSize(1);
        assertThat(typeParameter.getBounds()[0].getTypeName()).isEqualTo(Object.class.getTypeName());
    }

    @Test
    @SneakyThrows
    @Order(4)
    @DisplayName("Field \"value\" is \"T\"")
    void valueFieldIsGeneric() {
        var valueField = Box.class.getDeclaredField("value");
        var genericType = valueField.getGenericType();

        assertThat(genericType.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @SneakyThrows
    @Order(5)
    @DisplayName("Constructor parameter type is \"T\"")
    void constructorParameterIsGeneric() {
        var constructor = Box.class.getDeclaredConstructors()[0];
        assert (constructor.getParameters().length == 1);
        var parameter = constructor.getParameters()[0];

        assertThat(parameter.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @SneakyThrows
    @Order(6)
    @DisplayName("Getter return type is \"T\"")
    void getterReturnTypeIsGeneric() {
        var getter = Box.class.getDeclaredMethod("getValue");

        assertThat(getter.getGenericReturnType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @SneakyThrows
    @Order(7)
    @DisplayName("Setter parameter type is \"T\"")
    void setterParameterIsGeneric() {
        var setter = Box.class.getDeclaredMethod("setValue", Object.class);
        assert (setter.getParameters().length == 1);
        var parameter = setter.getParameters()[0];

        assertThat(parameter.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }
}


================================================
FILE: 1-0-java-basics/1-3-1-crazy-generics/README.MD
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Crazy Generics
#### Learn tricky nuances and become the master of type parametrization in Java 💪

### WHY ❓
Generics in Java provide **safe type parametrization.** Sou can write the logic of a method or a whole class and 
**reuse it for different types**. Before Java 5 (when generics were introduced), common logic could be reused as well. 
However, it required **using `Object` everywhere and casting it everytime you want to get an actual type**.
It **could easily cause `ClassCastException` at runtime which is not safe.**  When using generics, it's still the same `Object`,
but **now compiler creates casts for you implicitly, and makes sure you didn't mess up with types**, which **moves errors from
runtime to compile time and makes type parametrization safer.**

### Objectives
* create a generic class with **simple type parameter** ✅
* create a generic class with **bounded type parameter** ✅
* create a generic class with **multiples type parameter** ✅
* create a generic class with **recursively bounded type parameter** ✅
* create a **generic method** ✅
* create a method that accepts a generic class on any type using **wildcard** ✅
* create method parameter using **bounded wildcard** ✅

### Related materials ℹ️
* [Effective Java, Chapter 5 – Generics](https://read.amazon.com/kp/embed?asin=B078H61SCH&preview=newtab&linkCode=kpe&ref_=cm_sw_r_kb_dp_SADNB2C41TWARGY4QGKZ) 📘

### Exercise overview 🇺🇦
[![Watch the video](https://img.youtube.com/vi/EaL5KsSlEQM/0.jpg)](https://www.youtube.com/watch?v=EaL5KsSlEQM)

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 1-0-java-basics/1-3-1-crazy-generics/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>1-0-java-basics</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>1-3-1-crazy-generics</artifactId>
    
</project>

================================================
FILE: 1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java
================================================
package com.bobocode.basics;

import com.bobocode.basics.util.BaseEntity;
import com.bobocode.util.ExerciseNotCompletedException;
import lombok.Data;

import java.io.Serializable;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
 * {@link CrazyGenerics} is an exercise class. It consists of classes, interfaces and methods that should be updated
 * using generics.
 * <p>
 * TODO: go step by step from top to bottom. Read the java doc, write code and run CrazyGenericsTest to verify your impl
 * <p>
 * Hint: in some cases you will need to refactor the code, like replace {@link Object} with a generic type. In order
 * cases you will need to add new fields, create new classes, or add new methods. Always try to read java doc and update
 * the code according to it.
 * <p><p>
 * <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com">visit our website</a></strong>
 * <p>
 *
 * @author Taras Boychuk
 */
public class CrazyGenerics {
    /**
     * {@link Sourced} is a container class that allows storing any object along with the source of that data.
     * The value type can be specified by a type parameter "T".
     *
     * @param <T> – value type
     */
    @Data
    public static class Sourced { // todo: refactor class to introduce type parameter and make value generic
        private Object value;
        private String source;
    }

    /**
     * {@link Limited} is a container class that allows storing an actual value along with possible min and max values.
     * It is special form of triple. All three values have a generic type that should be a subclass of {@link Number}.
     *
     * @param <T> – actual, min and max type
     */
    @Data
    public static class Limited {
        // todo: refactor class to introduce type param bounded by number and make fields generic numbers
        private final Object actual;
        private final Object min;
        private final Object max;
    }

    /**
     * {@link Converter} interface declares a typical contract of a converter. It works with two independent generic types.
     * It defines a convert method which accepts one parameter of one type and returns a converted result of another type.
     *
     * @param <T> – source object type
     * @param <R> - converted result type
     */
    public interface Converter { // todo: introduce type parameters
        // todo: add convert method
    }

    /**
     * {@link MaxHolder} is a container class that keeps track of the maximum value only. It works with comparable objects
     * and allows you to put new values. Every time you put a value, it is stored only if the new value is greater
     * than the current max.
     *
     * @param <T> – value type
     */
    public static class MaxHolder { // todo: refactor class to make it generic
        private Object max;

        public MaxHolder(Object max) {
            this.max = max;
        }

        /**
         * Puts a new value to the holder. A new value is stored to the max, only if it is greater than current max value.
         *
         * @param val a new value
         */
        public void put(Object val) {
            throw new ExerciseNotCompletedException(); // todo: update parameter and implement the method
        }

        public Object getMax() {
            return max;
        }
    }

    /**
     * {@link StrictProcessor} defines a contract of a processor that can process only objects that are {@link Serializable}
     * and {@link Comparable}.
     *
     * @param <T> – the type of objects that can be processed
     */
    interface StrictProcessor { // todo: make it generic
        void process(Object obj);
    }

    /**
     * {@link CollectionRepository} defines a contract of a runtime store for entities based on any {@link Collection}.
     * It has methods that allow to save new entity, and get whole collection.
     *
     * @param <T> – a type of the entity that should be a subclass of {@link BaseEntity}
     * @param <C> – a type of any collection
     */
    interface CollectionRepository { // todo: update interface according to the javadoc
        void save(Object entity);

        Collection<Object> getEntityCollection();
    }

    /**
     * {@link ListRepository} extends {@link CollectionRepository} but specifies the underlying collection as
     * {@link java.util.List}.
     *
     * @param <T> – a type of the entity that should be a subclass of {@link BaseEntity}
     */
    interface ListRepository { // todo: update interface according to the javadoc
    }

    /**
     * {@link ComparableCollection} is a {@link Collection} that can be compared by size. It extends a {@link Collection}
     * interface and {@link Comparable} interface, and provides a default implementation of a compareTo method that
     * compares collections sizes.
     * <p>
     * Please note that size does not depend on the elements type, so it is allowed to compare collections of different
     * element types.
     *
     * @param <E> a type of collection elements
     */
    interface ComparableCollection { // todo: refactor it to make generic and provide a default impl of compareTo
    }

    /**
     * {@link CollectionUtil} is an util class that provides various generic helper methods.
     */
    static class CollectionUtil {
        static final Comparator<BaseEntity> CREATED_ON_COMPARATOR = Comparator.comparing(BaseEntity::getCreatedOn);

        /**
         * An util method that allows to print a dashed list of elements
         *
         * @param list
         */
        public static void print(List<Integer> list) {
            // todo: refactor it so the list of any type can be printed, not only integers
            list.forEach(element -> System.out.println(" – " + element));
        }

        /**
         * Util method that check if provided collection has new entities. An entity is any object
         * that extends {@link BaseEntity}. A new entity is an entity that does not have an id assigned.
         * (In other word, which id value equals null).
         *
         * @param entities provided collection of entities
         * @return true if at least one of the elements has null id
         */
        public static boolean hasNewEntities(Collection<BaseEntity> entities) {
            throw new ExerciseNotCompletedException(); // todo: refactor parameter and implement method
        }

        /**
         * Util method that checks if a provided collection of entities is valid. An entity is any subclass of
         * a {@link BaseEntity} A validation criteria can be different for different cases, so it is passed
         * as second parameter.
         *
         * @param entities            provided collection of entities
         * @param validationPredicate criteria for validation
         * @return true if all entities fit validation criteria
         */
        public static boolean isValidCollection() {
            throw new ExerciseNotCompletedException(); // todo: add method parameters and implement the logic
        }

        /**
         * hasDuplicates is a generic util method checks if a list of entities contains target entity more than once.
         * In other words, it checks if target entity has duplicates in the provided list. A duplicate is an entity that
         * has the same UUID.
         *
         * @param entities     given list of entities
         * @param targetEntity a target entity
         * @param <T>          entity type
         * @return true if entities list contains target entity more than once
         */
        public static boolean hasDuplicates() {
            throw new ExerciseNotCompletedException(); // todo: update method signature and implement it
        }

        /**
         * findMax is a generic util method that accepts an {@link Iterable} and {@link Comparator} and returns an
         * optional object, that has maximum "value" based on the given comparator.
         *
         * @param elements   provided iterable of elements
         * @param comparator an object that will be used to compare elements
         * @param <T>        type of elements
         * @return optional max value
         */
        // todo: create a method and implement its logic manually without using util method from JDK

        /**
         * findMostRecentlyCreatedEntity is a generic util method that accepts a collection of entities and returns the
         * one that is the most recently created. If collection is empty,
         * it throws {@link java.util.NoSuchElementException}.
         * <p>
         * This method reuses findMax method and passes entities along with prepare comparator instance,
         * that is stored as constant CREATED_ON_COMPARATOR.
         *
         * @param entities provided collection of entities
         * @param <T>      entity type
         * @return an entity from the given collection that has the max createdOn value
         */
        // todo: create a method according to JavaDoc and implement it using previous method

        /**
         * An util method that allows to swap two elements of any list. It changes the list so the element with the index
         * i will be located on index j, and the element with index j, will be located on the index i.
         * Please note that in order to make it convenient and simple, it DOES NOT declare any type parameter.
         *
         * @param elements a list of any given type
         * @param i        index of the element to swap
         * @param j        index of the other element to swap
         */
        public static void swap(List<?> elements, int i, int j) {
            Objects.checkIndex(i, elements.size());
            Objects.checkIndex(j, elements.size());
            throw new ExerciseNotCompletedException(); // todo: complete method implementation 
        }

    }
}


================================================
FILE: 1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/util/BaseEntity.java
================================================
package com.bobocode.basics.util;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.UUID;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public abstract class BaseEntity {
    protected UUID uuid;
    protected LocalDateTime createdOn;

    public BaseEntity(UUID uuid) {
        this.uuid = uuid;
        this.createdOn = LocalDateTime.now();
    }
}


================================================
FILE: 1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java
================================================
package com.bobocode.basics;

import com.bobocode.basics.CrazyGenerics.*;
import com.bobocode.basics.util.BaseEntity;
import lombok.SneakyThrows;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static java.time.temporal.ChronoUnit.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.when;

/**
 * A reflection-based test class for {@link CrazyGenerics}.
 * <p>
 * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.
 *
 * @author Taras Boychuk
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CrazyGenericsTest {
    final String TYPE_PARAMETER_NAME = "T";
    final String SECOND_TYPE_PARAMETER_NAME = "R";
    final String COLLECTION_ELEMENT_TYPE_PARAMETER_NAME = "E";

    @Test
    @Order(1)
    @DisplayName("Sourced class has one type parameter")
    void sourcedClassHasOneTypeParameter() {
        var typeParameters = Sourced.class.getTypeParameters();

        assertThat(typeParameters).hasSize(1);
    }

    @Test
    @Order(2)
    @DisplayName("Sourced class type parameter is called \"T\"")
    void sourcedClassTypeParameterIsCalledT() {
        var typeParameter = Sourced.class.getTypeParameters()[0];

        assertThat(typeParameter.getName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(3)
    @DisplayName("Sourced class type parameter \"T\" is not bounded")
    void sourcedClassTypeParameterIsNotBounded() {
        var typeParameter = Sourced.class.getTypeParameters()[0];

        assertThat(typeParameter.getBounds()).hasSize(1);
        assertThat(typeParameter.getBounds()[0].getTypeName()).isEqualTo(Object.class.getTypeName());
    }

    @Test
    @SneakyThrows
    @Order(4)
    @DisplayName("Sourced class field \"value\" has generic type \"T\"")
    void valueFieldIsGeneric() {
        var valueField = Sourced.class.getDeclaredField("value");
        var genericType = valueField.getGenericType();

        assertThat(genericType.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }


    @Test
    @Order(5)
    @DisplayName("Limited class has one type parameter")
    void limitedClassHasOneTypeParameter() {
        var typeParameters = Limited.class.getTypeParameters();

        assertThat(typeParameters).hasSize(1);
    }

    @Test
    @Order(6)
    @DisplayName("Limited class type parameter is bounded by Number")
    void limitedClassTypeParameterIsBoundedByNumber() {
        var typeParameters = Limited.class.getTypeParameters();
        var typeParam = typeParameters[0];
        assert (typeParam.getBounds().length == 1);
        var boundType = typeParam.getBounds()[0];

        assertThat(boundType.getTypeName()).isEqualTo(Number.class.getTypeName());
    }

    @Test
    @SneakyThrows
    @Order(7)
    @DisplayName("Limited class fields have generic type \"T\"")
    void limitedClassFieldsAreGeneric() {
        var fields = Limited.class.getDeclaredFields();

        for (var f : fields) {
            assertThat(f.getGenericType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
        }
    }

    @Test
    @Order(8)
    @DisplayName("Converter interface has two type parameters")
    void converterInterfaceHasTwoTypeParameters() {
        var typeParameters = Converter.class.getTypeParameters();

        assertThat(typeParameters).hasSize(2);
    }

    @Test
    @Order(9)
    @DisplayName("Converter interface first type parameter is called \"T\"")
    void converterInterfaceFirstTypeParameterIsCalledT() {
        var typeParameters = Converter.class.getTypeParameters();
        var firstTypeParam = typeParameters[0];

        assertThat(firstTypeParam.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(10)
    @DisplayName("Converter interface second type parameter is called \"R\"")
    void converterInterfaceSecondTypeParameterIsCalledR() {
        var typeParameters = Converter.class.getTypeParameters();
        var firstTypeParam = typeParameters[1];

        assertThat(firstTypeParam.getTypeName()).isEqualTo(SECOND_TYPE_PARAMETER_NAME);
    }

    @Test
    @SneakyThrows
    @Order(11)
    @DisplayName("Convert method parameter \"obj\" has type \"T\"")
    void converterMethodParameterHasTypeT() {
        var convertMethod = Converter.class.getDeclaredMethod("convert", Object.class);
        var objParam = convertMethod.getParameters()[0];

        assertThat(objParam.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @SneakyThrows
    @Order(12)
    @DisplayName("Convert method return type is \"R\"")
    void converterMethodReturnTypeIsR() {
        var convertMethod = Converter.class.getDeclaredMethod("convert", Object.class);

        assertThat(convertMethod.getGenericReturnType().getTypeName()).isEqualTo(SECOND_TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(13)
    @DisplayName("MaxHolder class has one type parameter")
    void maxHolderClassHasOneTypeParameter() {
        var typeParameters = MaxHolder.class.getTypeParameters();

        assertThat(typeParameters).hasSize(1);
    }

    @Test
    @Order(14)
    @DisplayName("MaxHolder class type parameter is called \"T\"")
    void maxHolderClassTypeParameterIsCalledT() {
        var typeParameter = MaxHolder.class.getTypeParameters()[0];

        assertThat(typeParameter.getName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(15)
    @DisplayName("MaxHolder type parameter is bound by Comparable")
    void maxHolderClassTypeParameterShouldBeBoundByComparableT() {
        var typeParameters = MaxHolder.class.getTypeParameters();
        var typeParam = typeParameters[0];
        var boundType = typeParam.getBounds()[0];

        var expectedBoundTypeName = String.format("%s<? super %s>", Comparable.class.getTypeName(), TYPE_PARAMETER_NAME);
        assertThat(boundType.getTypeName()).isEqualTo(expectedBoundTypeName);
    }

    @Test
    @Order(16)
    @DisplayName("MaxHolder field has type \"T\"")
    void maxHolderFieldHasTypeT() {
        var typeParameter = MaxHolder.class.getTypeParameters()[0];

        assertThat(typeParameter.getName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(17)
    @SneakyThrows
    @DisplayName("MaxHolder constructor param has type \"T\"")
    void maxHolderConstructorParamHasTypeT() {
        var constructor = MaxHolder.class.getConstructors()[0];
        assert (constructor.getParameters().length == 1);
        var param = constructor.getParameters()[0];

        assertThat(param.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(18)
    @SneakyThrows
    @DisplayName("MaxHolder put method param has type \"T\"")
    void maxHolderPutMethodParamHasTypeT() {
        var putMethod = getMethodByName(MaxHolder.class, "put");
        var param = putMethod.getParameters()[0];

        assertThat(param.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(19)
    @SneakyThrows
    @DisplayName("MaxHolder getMax return type is \"T\"")
    void maxHolderGetMaxReturnTypeIsT() {
        var getMaxMethod = MaxHolder.class.getDeclaredMethod("getMax");

        assertThat(getMaxMethod.getGenericReturnType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(20)
    @SneakyThrows
    @DisplayName("MaxHolder put stores argument value when it's greater than max")
    void maxHolderPut() {
        Constructor<?> constructor = MaxHolder.class.getConstructors()[0];
        var maxHolder = constructor.newInstance(10);
        var putMethod = getMethodByName(MaxHolder.class, "put");
        var getMaxMethod = MaxHolder.class.getDeclaredMethod("getMax");

        putMethod.invoke(maxHolder, 11);

        assertThat(getMaxMethod.invoke(maxHolder)).isEqualTo(11);
    }

    @Test
    @Order(21)
    @SneakyThrows
    @DisplayName("MaxHolder put keeps old value when argument is less than max")
    void maxHolderPutWhenArgumentIsLessThanMax() {
        Constructor<?> constructor = MaxHolder.class.getConstructors()[0];
        var maxHolder = constructor.newInstance(10);
        var putMethod = getMethodByName(MaxHolder.class, "put");
        var getMaxMethod = MaxHolder.class.getDeclaredMethod("getMax");

        putMethod.invoke(maxHolder, 9);

        assertThat(getMaxMethod.invoke(maxHolder)).isEqualTo(10);
    }

    @Test
    @Order(22)
    @DisplayName("StrictProcessor class has one generic type")
    void strictProcessorHasOneGenericType() {
        var typeParameters = StrictProcessor.class.getTypeParameters();

        assertThat(typeParameters).hasSize(1);
    }

    @Test
    @Order(23)
    @DisplayName("StrictProcessor type parameter is called \"T\"")
    void strictProcessorTypeParameterIsCalledT() {
        var typeParameters = StrictProcessor.class.getTypeParameters();
        var typePram = typeParameters[0];

        assertThat(typePram.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(24)
    @DisplayName("StrictProcessor type parameter is bound by both Serializable and Comparable<T>")
    void strictProcessorTypeParameterIsBoundBySerializableAndComparable() {
        var typeParameters = StrictProcessor.class.getTypeParameters();
        var typeParam = typeParameters[0];
        assertThat(typeParam.getBounds())
                .hasSize(2)
                .extracting(Type::getTypeName)
                .containsExactlyInAnyOrder(Serializable.class.getTypeName(),
                        String.format("%s<? super %s>", Comparable.class.getTypeName(), TYPE_PARAMETER_NAME));
    }

    @Test
    @Order(25)
    @DisplayName("StrictProcessor process method parameter has type \"T\"")
    void strictProcessorProcessMethodParameterHasTypeT() {
        var processMethod = getMethodByName(StrictProcessor.class, "process");

        assert (processMethod.getParameters().length == 1);
        var processMethodParam = processMethod.getParameters()[0];

        assertThat(processMethodParam.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(26)
    @DisplayName("CollectionRepository has two type parameters")
    void collectionRepositoryHasTwoTypeParameters() {
        var typeParameters = CollectionRepository.class.getTypeParameters();

        assertThat(typeParameters).hasSize(2);
    }

    @Test
    @Order(27)
    @DisplayName("CollectionRepository first type parameter is called \"T\"")
    void collectionRepositoryFirstTypeParameterIsCalledT() {
        var typeParam = CollectionRepository.class.getTypeParameters()[0];

        assertThat(typeParam.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(28)
    @DisplayName("CollectionRepository first type parameter is bounded by BaseEntity")
    void collectionRepositoryFirstTypeParameterIsBoundedByBaseEntity() {
        var typeParam = CollectionRepository.class.getTypeParameters()[0];
        var boundType = typeParam.getBounds()[0];

        assertThat(boundType.getTypeName()).isEqualTo(BaseEntity.class.getTypeName());
    }

    @Test
    @Order(29)
    @DisplayName("CollectionRepository second type parameter is called \"C\"")
    void collectionRepositorySecondTypeParameterIsCalledT() {
        var typeParam = CollectionRepository.class.getTypeParameters()[1];

        assertThat(typeParam.getTypeName()).isEqualTo("C");
    }

    @Test
    @Order(30)
    @DisplayName("CollectionRepository second type parameter is bounded by Collection<T>")
    void collectionRepositorySecondTypeParameterIsBoundedByCollection() {
        var typeParam = CollectionRepository.class.getTypeParameters()[1];
        var boundType = typeParam.getBounds()[0];

        assertThat(boundType.getTypeName())
                .isEqualTo(String.format("%s<T>", Collection.class.getTypeName()));
    }

    @Test
    @Order(31)
    @DisplayName("CollectionRepository save method param has type \"T\"")
    @SneakyThrows
    void collectionRepositorySaveMethodParameterHasTypeT() {
        var saveMethod = getMethodByName(CollectionRepository.class, "save");

        var methodParam = saveMethod.getParameters()[0];

        assertThat(methodParam.getParameterizedType().getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(32)
    @DisplayName("CollectionRepository getEntityCollection method has return type \"C\"")
    @SneakyThrows
    void collectionRepositoryGetCollectionMethodHasReturnTypeC() {
        var getEntityCollectionMethod = CollectionRepository.class.getMethod("getEntityCollection");

        assertThat(getEntityCollectionMethod.getGenericReturnType().getTypeName()).isEqualTo("C");
    }

    @Test
    @Order(33)
    @DisplayName("ListRepository extends CollectionRepository")
    void listRepositoryExtendsCollectionRepository() {
        var superInterface = ListRepository.class.getInterfaces()[0];

        assertThat(superInterface.getTypeName()).isEqualTo(CollectionRepository.class.getTypeName());
    }

    @Test
    @Order(34)
    @DisplayName("ListRepository has one type parameter")
    void listRepositoryHasOneTypeParameter() {
        var typeParams = ListRepository.class.getTypeParameters();

        assertThat(typeParams).hasSize(1);
    }

    @Test
    @Order(35)
    @DisplayName("ListRepository type parameter is called \"T\"")
    void listRepositoryTypeParameterIsCalledT() {
        var typeParam = ListRepository.class.getTypeParameters()[0];

        assertThat(typeParam.getTypeName()).isEqualTo(TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(36)
    @DisplayName("ListRepository type parameter is bound by BaseEntity")
    void listRepositoryTypeParameterIsBoundByBaseEntity() {
        var typeParam = ListRepository.class.getTypeParameters()[0];
        var boundType = typeParam.getBounds()[0];

        assertThat(boundType.getTypeName()).isEqualTo(BaseEntity.class.getTypeName());
    }

    @Test
    @Order(37)
    @DisplayName("ListRepository parent interface has specified two types")
    void listRepositoryParentInterfaceHasSpecifiedTwoTypes() {
        var collectionInterface = ListRepository.class.getGenericInterfaces()[0];

        assertThat(collectionInterface.getTypeName())
                .isEqualTo(String.format("%s<T, %s<T>>", CollectionRepository.class.getTypeName(), List.class.getTypeName()));
    }

    @Test
    @Order(38)
    @DisplayName("ComparableCollection has one type parameter \"E\"")
    void comparableCollectionIsGeneric() {
        var typeParameters = ComparableCollection.class.getTypeParameters();

        assertThat(typeParameters).hasSize(1);
        assertThat(typeParameters[0].getName()).isEqualTo(COLLECTION_ELEMENT_TYPE_PARAMETER_NAME);
    }

    @Test
    @Order(39)
    @DisplayName("ComparableCollection extends Collection")
    void comparableCollectionExtendsCollection() {
        var collectionInterface = Arrays.stream(ComparableCollection.class.getInterfaces())
                .filter(it -> it.getTypeName().equals(Collection.class.getTypeName()))
                .findAny()
                .orElseThrow();

        assertThat(collectionInterface).isNotNull();
    }

    @Test
    @Order(40)
    @DisplayName("Type parameter is specified for a super interface Collection")
    void comparableCollectionExtendsCollectionOfTheSameElementsType() {
        var collectionInterface = Arrays.stream(ComparableCollection.class.getGenericInterfaces())
                .filter(it -> it.getTypeName().equals(
                        String.format("%s<%s>", Collection.class.getTypeName(), COLLECTION_ELEMENT_TYPE_PARAMETER_NAME)
                ))
                .findAny()
                .orElseThrow();

        assertThat(collectionInterface).isNotNull();
    }

    @Test
    @Order(41)
    @DisplayName("ComparableCollection extends Comparable")
    void comparableCollectionExtendsComparable() {
        var comparableInterface = Arrays.stream(ComparableCollection.class.getInterfaces())
                .filter(it -> it.getTypeName().equals(Comparable.class.getTypeName()))
                .findAny()
                .orElseThrow();

        assertThat(comparableInterface).isNotNull();
    }

    @Test
    @Order(42)
    @DisplayName("Collection of any type is specified as type parameter for a super interface Comparable")
    void comparableCollectionExtendsComparableOfCollectionOfAnyType() {
        var comparableInterface = Arrays.stream(ComparableCollection.class.getGenericInterfaces())
                .filter(it -> it.getTypeName().equals(
                        String.format("%s<%s<?>>", Comparable.class.getTypeName(), Collection.class.getTypeName())
                ))
                .findAny()
                .orElseThrow();

        assertThat(comparableInterface).isNotNull();
    }

    @Test
    @Order(43)
    @DisplayName("Method compareTo is overridden")
    void comparableCollectionOverridesCompareToMethod() {
        var compareToMethod = Arrays.stream(ComparableCollection.class.getDeclaredMethods())
                .filter(m -> m.getName().equals("compareTo"))
                .findAny();

        assertThat(compareToMethod).isPresent();
    }

    @Test
    @Order(44)
    @DisplayName("ComparableCollection provides a default impl of compareTo method")
    void compareToProvidesDefaultImpl() {
        var compareToMethod = getMethodByName(ComparableCollection.class, "compareTo");

        assertThat(compareToMethod.isDefault()).isTrue();
    }

    @Test
    @Order(45)
    @DisplayName("A parameter of method compareTo is a collection of elements of any type")
    void compareToParamIsACollectionOfAnyType() {
        var compareToMethod = getMethodByName(ComparableCollection.class, "compareTo");
        var collectionParam = compareToMethod.getParameters()[0];

        assertThat(collectionParam.getParameterizedType().getTypeName())
                .isEqualTo(String.format("%s<?>", Collection.class.getTypeName()));
    }

    @Order(46)
    @DisplayName("Method compareTo compares collection size")
    @SneakyThrows
    @ParameterizedTest
    @ValueSource(ints = {0, 5, 10})
    void compareToComparesSize(int size) {
        var compareToMethod = getMethodByName(ComparableCollection.class, "compareTo");
        var compCollectionMock = Mockito.spy(ComparableCollection.class);
        var sizeMethod = getMethodByName(ComparableCollection.class, "size");

        when(sizeMethod.invoke(compCollectionMock)).thenReturn(size);
        var list = List.of(1, 2, 3, 4, 5);

        assertThat(compareToMethod.invoke(compCollectionMock, list))
                .isEqualTo(Integer.compare(size, list.size()));
    }

    @Test
    @Order(47)
    @DisplayName("CollectionUtil is not a generic class")
    void collectionUtilIsNotAGenericClass() {
        var typeParams = CollectionUtil.class.getTypeParameters();

        assertThat(typeParams).isEmpty();
    }

    @Test
    @Order(48)
    @DisplayName("Method print param is a list of any type declared as unbounded wildcard")
    void printParamIsAListOfAnyType() {
        var printMethod = getMethodByName(CollectionUtil.class, "print");
        var listParam = printMethod.getParameters()[0];
        var typeName = listParam.getParameterizedType().getTypeName();

        assertThat(typeName).isEqualTo(String.format("%s<?>", List.class.getTypeName()));

    }

    @Test
    @Order(49)
    @DisplayName("Method hasNewEntities accepts a collection of any entities (BaseEntity subclasses)")
    void hasNewEntitiesMethodParamIsAGenericCollectionOfEntities() {
        var hasNewEntitiesMethod = getMethodByName(CollectionUtil.class, "hasNewEntities");

        var entitiesParam = hasNewEntitiesMethod.getParameters()[0];

        assertThat(entitiesParam.getParameterizedType().getTypeName())
                .isEqualTo(String.format("%s<? extends %s>", Collection.class.getTypeName(), BaseEntity.class.getTypeName()));
    }

    @ParameterizedTest
    @Order(50)
    @MethodSource("hasNewEntitiesArgs")
    @DisplayName("Method hasNewEntities checks id values")
    @SneakyThrows
    void hasNewEntitiesChecksIds(Collection<BaseEntity> entities, boolean result) {
        var hasNewEntitiesMethod = getMethodByName(CollectionUtil.class, "hasNewEntities");

        boolean hasNewEntities = (boolean) hasNewEntitiesMethod.invoke(null, entities);

        assertThat(hasNewEntities).isEqualTo(result);
    }

    private Method getMethodByName(Class<?> clazz, String methodName) {
        return Arrays.stream(clazz.getMethods())
                .filter(m -> m.getName().equals(methodName))
                .findAny().orElseThrow();
    }

    static Stream<Arguments> hasNewEntitiesArgs() {
        var newEntity = Mockito.mock(BaseEntity.class);
        when(newEntity.getUuid()).thenReturn(null);
        var oldEntity = Mockito.mock(BaseEntity.class);
        when(oldEntity.getUuid()).thenReturn(UUID.randomUUID());

        return Stream.of(
                arguments(List.of(newEntity, newEntity), true),
                arguments(List.of(newEntity, oldEntity), true),
                arguments(List.of(oldEntity, oldEntity), false)
        );
    }

    @Test
    @Order(51)
    @DisplayName("Method isValidCollection accepts a collection of any entities as a first param")
    void isValidCollectionMethodFirstParamIsAGenericCollectionOfEntities() {
        var isValidCollectionMethod = getMethodByName(CollectionUtil.class, "isValidCollection");

        var entitiesParam = isValidCollectionMethod.getParameters()[0];

        assertThat(entitiesParam.getParameterizedType().getTypeName())
                .isEqualTo(String.format("%s<? extends %s>", Collection.class.getTypeName(), BaseEntity.class.getTypeName()));
    }

    @Test
    @Order(52)
    @DisplayName("Method isValidCollection accepts a predicate of any BaseEntity superclasses as a second param")
    void isValidCollectionMethodSecondParamIsAnyBaseEntitySuperClass() {
        var isValidCollectionMethod = getMethodByName(CollectionUtil.class, "isValidCollection");

        var validationPredicate = isValidCollectionMethod.getParameters()[1];

        assertThat(validationPredicate.getParameterizedType().getTypeName())
                .isEqualTo(String.format("%s<? super %s>", Predicate.class.getTypeName(), BaseEntity.class.getTypeName()));
    }

    @ParameterizedTest
    @MethodSource("isValidCollectionArgs")
    @Order(53)
    @DisplayName("Method isValidCollection returns true when all entities pass validation predicate")
    @SneakyThrows
    void isValidCollectionReturnsTrueWhenAllEntitiesPassValidationPredicate(List<TestEntity> entities,
                                                                            Predicate<TestEntity> validationPredicate,
                                                                            boolean isValid) {
        var isValidCollectionMethod = getMethodByName(CollectionUtil.class, "isValidCollection");

        boolean result = (boolean) isValidCollectionMethod.invoke(null, entities, validationPredicate);

        assertThat(result).isEqualTo(isValid);
    }

    private static Stream<Arguments> isValidCollectionArgs() {
        var now = LocalDateTime.now();
        Predicate<BaseEntity> notNullIdValidationPredicate = e -> e.getUuid() != null;
        Predicate<BaseEntity> pastCreatedDateValidationPredicate = e -> e.getCreatedOn().compareTo(now) < 0;

        return Stream.of(
                arguments(List.of(new TestEntity(), new TestEntity()), notNullIdValidationPredicate, true),
                arguments(List.of(new TestEntity(), new TestEntity((UUID) null)), notNullIdValidationPredicate, false),
                arguments(
                        List.of(
                                new TestEntity(now.minus(1, DAYS)),
                                new TestEntity((now.minus(1, MONTHS)))
                        ),
                        pastCreatedDateValidationPredicate,
                        true
                ),
                arguments(
                        List.of(
                                new TestEntity(now.minus(1, DAYS)),
                                new TestEntity((now.plus(2, DAYS)))
                        ),
                        pastCreatedDateValidationPredicate,
                        false
                )
        );
    }


    @Test
    @Order(54)
    @DisplayName("hasDuplicates is a generic method")
    void hasDuplicatesIsAGenericMethod() {
        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, "hasDuplicates");

        assertThat(hasDuplicatesMethod.getTypeParameters()).hasSize(1);
    }

    @Test
    @Order(55)
    @DisplayName("hasDuplicates type parameter is called \"T\"")
    void hasDuplicatesTypeParameterIsCalledT() {
        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, "hasDuplicates");
        var typeParameter = hasDuplicatesMethod.getTypeParameters()[0];

        assertThat(typeParameter.getName()).isEqualTo("T");
    }

    @Test
    @Order(56)
    @DisplayName("hasDuplicates type parameter is bounded by BaseEntity")
    void hasDuplicatesTypeParameterIsBoundedByBaseEntity() {
        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, "hasDuplicates");
        var typeParameter = hasDuplicatesMethod.getTypeParameters()[0];
        Type bound = typeParameter.getBounds()[0];

        assertThat(bound.getTypeName()).isEqualTo(BaseEntity.class.getTypeName());
    }

    @ParameterizedTest
    @Order(57)
    @MethodSource("hasDuplicatesArgs")
    @DisplayName("hasDuplicates checks entity duplicates by UUID")
    @SneakyThrows
    void hasDuplicatesChecksEntitiesByUUID(List<? extends BaseEntity> entities, BaseEntity targetEntity, Boolean hasDuplicates) {
        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, "hasDuplicates");

        var result = hasDuplicatesMethod.invoke(null, entities, targetEntity);

        assertThat(result).isEqualTo(hasDuplicates);
    }

    static Stream<Arguments> hasDuplicatesArgs() {
        var uniqueEntity = new TestEntity(UUID.randomUUID());
        var duplicateId = UUID.randomUUID();
        var duplicateEntity1 = new TestEntity(duplicateId);
        var duplicateEntity2 = new TestEntity(duplicateId);

        return Stream.of(
                arguments(List.of(uniqueEntity, duplicateEntity1), uniqueEntity, false),
                arguments(List.of(uniqueEntity, duplicateEntity1, duplicateEntity2), duplicateEntity1, true),
                arguments(List.of(duplicateEntity1, duplicateEntity2), duplicateEntity1, true)
        );
    }

    @Test
    @Order(58)
    @DisplayName("Method findMax has public access type")
    void findMaxHasPublicAccessType() {
        var findMaxMethod = getMethodByName(CollectionUtil.class, "findMax");
        assertThat(findMaxMethod).isNotNull();
    }

    @ParameterizedTest
    @Order(59)
    @MethodSource("findMaxArgs")
    @DisplayName("Method findMax returns the max value based on given comparator")
    @SneakyThrows
    void findMaxMethodReturnMaxValue(List<?> elements, Comparator<?> comparator, Object maxElement) {
        var findMaxMethod = getMethodByName(CollectionUtil.class, "findMax");

        var result = findMaxMethod.invoke(null, elements, comparator);

        assertThat(result).isEqualTo(Optional.of(maxElement));
    }

    static Stream<Arguments> findMaxArgs() {
        var maxEntity = new TestEntity(LocalDateTime.now().plus(10, DAYS));
        var entities = new ArrayList<>(List.of(new TestEntity(), new TestEntity(), maxEntity, new TestEntity()));
        var entityCreatedOnComparator = Comparator.comparing(BaseEntity::getCreatedOn);
        var maxNumber = 100;
        var numbers = List.of(maxNumber, 5, 20);
        var intComparator = Comparator.comparingInt(Integer::intValue);
        var maxWord = "what's up?";
        var greetings = List.of("hey", maxWord, "hello");
        var lengthComparator = Comparator.comparing(String::length);

        return Stream.of(
                arguments(entities, entityCreatedOnComparator, maxEntity),
                arguments(numbers, intComparator, maxNumber),
                arguments(greetings, lengthComparator, maxWord)
        );
    }

    @Test
    @Order(60)
    @DisplayName("Method findMostRecentlyCreatedEntity has public access type")
    void findMostRecentlyCreatedEntityHasPublicAccessType() {
        var findMostRecentlyCreatedEntity = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity");
        assertThat(findMostRecentlyCreatedEntity).isNotNull();
    }

    @Test
    @Order(61)
    @DisplayName("findMostRecentlyCreatedEntity is a generic method that accepts a collection of entities")
    void findMostRecentlyCreatedEntityIsAGenericMethod() {
        var hasDuplicatesMethod = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity");
        var typeParameter = hasDuplicatesMethod.getTypeParameters()[0];
        var bound = typeParameter.getBounds()[0];

        assertThat(typeParameter.getName()).isEqualTo("T");
        assertThat(bound.getTypeName()).isEqualTo(BaseEntity.class.getTypeName());
    }

    @ParameterizedTest
    @Order(62)
    @MethodSource("findMostRecentlyCreatedEntityArgs")
    @DisplayName("findMostRecentlyCreatedEntity returns the most recently created entity")
    @SneakyThrows
    void findMostRecentlyCreatedEntityReturnsEntityWithMaxCreatedOnValue(List<? extends BaseEntity> entities,
                                                                         BaseEntity mostRecentlyCreatedEntity) {
        var findMostRecentlyCreatedEntityMethod = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity");

        var result = findMostRecentlyCreatedEntityMethod.invoke(null, entities);

        assertThat(result).isEqualTo(mostRecentlyCreatedEntity);
    }

    static Stream<Arguments> findMostRecentlyCreatedEntityArgs() {
        var yearAgoEntity = new TestEntity(LocalDateTime.now().minus(1, YEARS));
        var monthAgoEntity = new TestEntity(LocalDateTime.now().minus(1, MONTHS));
        var dayAgoEntity = new TestEntity(LocalDateTime.now().minus(1, DAYS));

        return Stream.of(
                arguments(List.of(yearAgoEntity, monthAgoEntity, dayAgoEntity), dayAgoEntity),
                arguments(List.of(yearAgoEntity, monthAgoEntity), monthAgoEntity),
                arguments(List.of(yearAgoEntity, dayAgoEntity), dayAgoEntity),
                arguments(List.of(dayAgoEntity, monthAgoEntity), dayAgoEntity)
        );
    }

    @Test
    @Order(63)
    @DisplayName("findMostRecentlyCreatedEntity throws exception when collection is empty")
    @SneakyThrows
    void findMostRecentlyCreatedEntityThrowsException() {
        var findMostRecentlyCreatedEntityMethod = getMethodByName(CollectionUtil.class, "findMostRecentlyCreatedEntity");

        assertThatThrownBy(() -> findMostRecentlyCreatedEntityMethod.invoke(null, Collections.emptyList()))
                .hasCauseInstanceOf(NoSuchElementException.class);
    }

    @Test
    @Order(64)
    @DisplayName("Method swap does not declare type parameter")
    void swapMethodDoesNotDeclareTypeParameter() {
        var swapMethod = getMethodByName(CollectionUtil.class, "swap");
        var typeParameters = swapMethod.getTypeParameters();

        assertThat(typeParameters).isEmpty();
    }

    @Test
    @Order(65)
    @DisplayName("Method swap change elements by indexes")
    @SneakyThrows
    void swapChangesElements() {
        var testEntity1 = new TestEntity();
        var testEntity2 = new TestEntity();
        var entities = new ArrayList<>(List.of(new TestEntity(), testEntity1, testEntity2, new TestEntity()));
        var swapMethod = getMethodByName(CollectionUtil.class, "swap");

        swapMethod.invoke(null, entities, 1, 2);

        assertThat(entities.get(1)).isEqualTo(testEntity2);
        assertThat(entities.get(2)).isEqualTo(testEntity1);
    }

    static class TestEntity extends BaseEntity {
        public TestEntity() {
            this(UUID.randomUUID());
        }

        public TestEntity(UUID uuid) {
            super(uuid);
        }

        public TestEntity(LocalDateTime createdOn) {
            super(UUID.randomUUID(), createdOn);
        }
    }
}


================================================
FILE: 1-0-java-basics/1-3-2-heterogeneous-max-holder/README.MD
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Heterogeneous Max Holder
#### Improve your generics-related skills implementing a multi-type max holder container 💪

### Objectives
* create a container class that uses *a type token* ✅
* declare a generic type parameter that is `Comparable` ✅
* implement a generic method put that stores max values by its type ✅
* overload a generic method put with a custom `Comparator` ✅
* implement a generic method getMax that retrieves a max value by its type ✅
* wrap a comparator instance to make it null-safe ✅

### Exercise overview 🇺🇦
[![Watch the video](https://img.youtube.com/vi/jQnGDgH-Zfg/0.jpg)](https://www.youtube.com/watch?v=jQnGDgH-Zfg)

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 1-0-java-basics/1-3-2-heterogeneous-max-holder/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>1-0-java-basics</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>1-3-2-heterogeneous-max-holder</artifactId>
    
</project>

================================================
FILE: 1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java
================================================
package com.bobocode.basics;

import java.util.Map;

/**
 * {@link HeterogeneousMaxHolder} is a multi-type container that holds maximum values per each type. It's kind of a
 * key/value map, where the key is a type and the value is the maximum among all values of this type that were put.
 * <p>
 * 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.
 * <p>
 * <p>
 * <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com">visit our website</a></strong>
 * <p>
 *
 * @author Taras Boychuk
 */
public class HeterogeneousMaxHolder {

    /**
     * A method put stores a provided value by its type, if the value is greater than the current maximum. In other words, the logic
     * of this method makes sure that only max value is stored and everything else is ignored.
     * <p>
     * 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
     * max is returned. Otherwise, nothing new is added, and the provided value is returned.
     * <p>
     * So technically, this method always stores the greater value and returns the smaller one.
     *
     * @param key   a provided value type
     * @param value a value to put
     * @param <T>   value type parameter
     * @return a smaller value among the provided value and the current maximum
     */
    // todo: implement a method according to javadoc

    /**
     * An overloaded method put implements the same logic using a custom comparator. A given comparator is wrapped with
     * a null-safe comparator, considering null smaller than any non-null object.
     * <p>
     * All arguments must not be null.
     *
     * @param key        a provided value type
     * @param value      a value to put
     * @param comparator a custom comparator for the given type
     * @param <T>        value type parameter
     * @return a smaller value among the provided value and the current maximum
     */
    // todo: implement a method according to javadoc

    /**
     * A method getMax returns a max value by the given type. If no value is stored by this type, then it returns null.
     *
     * @param key a provided value type
     * @param <T> value type parameter
     * @return current max value or null
     */
    // todo: implement a method according to javadoc
}


================================================
FILE: 1-0-java-basics/1-3-2-heterogeneous-max-holder/src/test/java/com/bobocode/basics/HeterogeneousMaxHolderTest.java
================================================
package com.bobocode.basics;

import com.bobocode.data.Accounts;
import com.bobocode.model.Account;
import lombok.SneakyThrows;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.mockito.Mockito;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;


@TestMethodOrder(OrderAnnotation.class)
class HeterogeneousMaxHolderTest {
    private HeterogeneousMaxHolder heterogeneousMaxHolder = new HeterogeneousMaxHolder();
    private HeterogeneousMaxHolder heterogeneousMaxHolderMock = Mockito.spy(HeterogeneousMaxHolder.class);

    @Test
    @Order(1)
    @DisplayName("A class does not declare type parameters")
    void classDoesNotDeclareTypeParameters() {
        var classTypeParams = HeterogeneousMaxHolder.class.getTypeParameters();

        assertThat(classTypeParams).isEmpty();
    }

    @Test
    @Order(2)
    @DisplayName("A class has one private field Map")
    void classHasOneField() {
        var fields = HeterogeneousMaxHolder.class.getDeclaredFields();

        assertThat(fields).hasSize(1);
        assertThat(fields[0].getType()).isEqualTo(Map.class);
    }

    @Test
    @Order(3)
    @DisplayName("A Map field declares type arguments")
    void mapFieldStoresTypeToObject() {
        var field = HeterogeneousMaxHolder.class.getDeclaredFields()[0];

        assertThat(field.getGenericType().getTypeName())
                .isEqualTo(Map.class.getTypeName() + "<%s, %s>", Class.class.getTypeName() + "<?>", Object.class.getTypeName());

    }

    @Test
    @Order(4)
    @DisplayName("put method exists")
    void putExists() {
        var putMethodExists = Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())
                .anyMatch(m -> m.getName().equals("put"));

        assertTrue(putMethodExists);
    }

    @Test
    @Order(5)
    @DisplayName("put method declares one type parameter T")
    void putDeclaresOneTypeParam() {
        var putMethod = getPutMethod();

        var methodTypeParameters = putMethod.getTypeParameters();
        assertThat(methodTypeParameters).hasSize(1);

        var typeParam = putMethod.getTypeParameters()[0];
        assertThat(typeParam.getName()).isEqualTo("T");
    }

    @Test
    @Order(6)
    @DisplayName("put method accepts type-safe key")
    void putMethodAcceptsTypeSafeKeyParameter() {
        var putMethod = getPutMethod();

        var typeParam = (ParameterizedType) putMethod.getGenericParameterTypes()[0];
        var typeArgument = typeParam.getActualTypeArguments()[0];

        assertThat(typeParam.getRawType()).isEqualTo(Class.class);
        assertThat(typeArgument.getTypeName()).isEqualTo("T");
    }

    @Test
    @Order(7)
    @DisplayName("put method accepts comparable value")
    void putMethodAcceptsComparableValueParameter() {
        var putMethod = getPutMethod();

        var typeParam = putMethod.getTypeParameters()[0];
        var boundType = (ParameterizedType) typeParam.getBounds()[0];

        assertThat(boundType.getRawType()).isEqualTo(Comparable.class);
    }

    @Test
    @Order(8)
    @DisplayName("put method supports value that has comparable super class")
    void putMethodAcceptsValueParameterWithComparableSuperClass() {
        var putMethod = getPutMethod();

        var typeParam = putMethod.getTypeParameters()[0];
        var boundType = (ParameterizedType) typeParam.getBounds()[0];
        var typeArgument = boundType.getActualTypeArguments()[0].getTypeName();

        assertThat(boundType.getRawType()).isEqualTo(Comparable.class);
        assertThat(typeArgument).isEqualTo("? super T");
    }

    @Test
    @Order(9)
    @SneakyThrows
    @DisplayName("put stores provided value when current max is null")
    void putStoresValueWhenCurrentMaxIsNull() {
        callPut(String.class, "I am maximum");
        var storedMaxValue = getMaxHelper(String.class);

        assertThat(storedMaxValue).isEqualTo("I am maximum");
    }

    @Test
    @Order(10)
    @SneakyThrows
    @DisplayName("put returns null when current max is null")
    void putReturnsNullWhenCurrentMaxIsNull() {
        var result = callPut(String.class, "I am maximum");

        assertThat(result).isNull();
    }

    @Test
    @Order(11)
    @SneakyThrows
    @DisplayName("put stores provided value when current max is smaller than it")
    void putStoresValueWhenCurrentMaxIsSmaller() {
        givenMaxHolderWithData(Long.class, 123L);

        callPut(Long.class, 222L);
        var storedMaxValue = getMaxHelper(Long.class);

        assertThat(storedMaxValue).isEqualTo(222L);
    }

    @Test
    @Order(12)
    @SneakyThrows
    @DisplayName("put returns old max value when the provided value is greater than it")
    void putReturnsOldMaxValue() {
        givenMaxHolderWithData(Long.class, 123L);

        var returnedValue = callPut(Long.class, 222L);

        assertThat(returnedValue).isEqualTo(123L);
    }

    @Test
    @Order(13)
    @SneakyThrows
    @DisplayName("put ignores provided value when the current max is greater than it")
    void putIgnoresNewValueWhenCurrentMaxIsGreater() {
        givenMaxHolderWithData(Long.class, 123L);

        callPut(Long.class, 101L);
        var storedMaxValue = getMaxHelper(Long.class);

        assertThat(storedMaxValue).isEqualTo(123L);
    }

    @Test
    @Order(14)
    @SneakyThrows
    @DisplayName("put returns provided value when the current max is greater than it")
    void putReturnsProvidedValueWhenCurrentMaxIsGreater() {
        givenMaxHolderWithData(Long.class, 123L);

        var returnedValue = callPut(Long.class, 101L);

        assertThat(returnedValue).isEqualTo(101L);
    }

    @Test
    @Order(15)
    @SneakyThrows
    @DisplayName("put method is overloaded with additional Comparator parameter")
    void putIsOverloadedWithAdditionalComparatorParam() {
        HeterogeneousMaxHolder.class.getMethod("put", Class.class, Object.class, Comparator.class);
    }

    @Test
    @Order(16)
    @DisplayName("Overloaded put method declares one type parameter T")
    void overloadedPutDeclaresOneTypeParam() {
        var putMethod = getOverloadedPutMethod();

        var methodTypeParameters = putMethod.getTypeParameters();
        assertThat(methodTypeParameters).hasSize(1);

        var typeParam = putMethod.getTypeParameters()[0];
        assertThat(typeParam.getName()).isEqualTo("T");
    }

    @Test
    @Order(17)
    @DisplayName("Overloaded put method accepts type-safe key")
    void overloadedPutMethodAcceptsTypeSafeKeyParameter() {
        var putMethod = getOverloadedPutMethod();

        var typeParam = (ParameterizedType) putMethod.getGenericParameterTypes()[0];
        var typeArgument = typeParam.getActualTypeArguments()[0];

        assertThat(typeParam.getRawType()).isEqualTo(Class.class);
        assertThat(typeArgument.getTypeName()).isEqualTo("T");
    }

    @Test
    @Order(18)
    @DisplayName("Overloaded put method accepts value of arbitrary type T")
    void overloadedPutMethodAcceptsAnyValue() {
        var putMethod = getOverloadedPutMethod();

        var genericValueTypeParam = putMethod.getGenericParameterTypes()[1];
        var actualValueTypeParm = putMethod.getParameterTypes()[1];

        assertThat(genericValueTypeParam.getTypeName()).isEqualTo("T");
        assertThat(actualValueTypeParm).isEqualTo(Object.class);
    }

    @Test
    @Order(19)
    @SneakyThrows
    @DisplayName("Overloaded put method supports comparator of a super type")
    void overloadedPutAcceptsComparatorOfSuperTypes() {
        var putMethod = HeterogeneousMaxHolder.class.getMethod("put", Class.class, Object.class, Comparator.class);

        var comparatorParam = (ParameterizedType) putMethod.getGenericParameterTypes()[2];
        var comparatorTypeArgument = comparatorParam.getActualTypeArguments()[0];

        assertThat(comparatorTypeArgument.getTypeName()).isEqualTo("? super T");
    }

    @Test
    @Order(20)
    @SneakyThrows
    @DisplayName("Overloaded put stores provided value when current max is null")
    void overloadedPutStoresValueWhenCurrentMaxIsNull() {
        var account = Accounts.generateAccount();
        callPut(Account.class, account, Comparator.comparing(Account::getBalance));
        var storedMaxValue = getMaxHelper(Account.class);

        assertThat(storedMaxValue).isEqualTo(account);
    }

    @Test
    @Order(21)
    @SneakyThrows
    @DisplayName("Overloaded put returns null when current max is null")
    void overloadedPutReturnsNullWhenCurrentMaxIsNull() {
        var result = callPut(Account.class, Accounts.generateAccount(), Comparator.comparing(Account::getBalance));

        assertThat(result).isNull();
    }

    @Test
    @Order(22)
    @SneakyThrows
    @DisplayName("Overloaded put stores provided value when current max is smaller than it")
    void overloadedPutStoresValueWhenCurrentMaxIsSmaller() {
        var givenAccount = Accounts.generateAccount();
        givenMaxHolderWithData(Account.class, givenAccount);
        var biggerBalanceAccount = Accounts.generateAccount();
        biggerBalanceAccount.setBalance(givenAccount.getBalance().add(BigDecimal.TEN));

        callPut(Account.class, biggerBalanceAccount, Comparator.comparing(Account::getBalance));
        var storedMaxValue = getMaxHelper(Account.class);

        assertThat(storedMaxValue).isEqualTo(biggerBalanceAccount);
    }

    @Test
    @Order(23)
    @SneakyThrows
    @DisplayName("Overloaded put returns old max value when the provided value is greater than it")
    void overloadedPutReturnsOldMaxValue() {
        var givenAccount = Accounts.generateAccount();
        givenMaxHolderWithData(Account.class, givenAccount);
        var biggerBalanceAccount = Accounts.generateAccount();
        biggerBalanceAccount.setBalance(givenAccount.getBalance().add(BigDecimal.TEN));

        var returnedValue = callPut(Account.class, biggerBalanceAccount, Comparator.comparing(Account::getBalance));

        assertThat(returnedValue).isEqualTo(givenAccount);
    }

    @Test
    @Order(24)
    @SneakyThrows
    @DisplayName("Overloaded put ignores provided value when the current max is greater than it")
    void overloadedPutIgnoresNewValueWhenCurrentMaxIsGreater() {
        var givenAccount = Accounts.generateAccount();
        givenMaxHolderWithData(Account.class, givenAccount);
        var smallerBalanceAccount = Accounts.generateAccount();
        smallerBalanceAccount.setBalance(givenAccount.getBalance().subtract(BigDecimal.TEN));

        callPut(Account.class, smallerBalanceAccount, Comparator.comparing(Account::getBalance));
        var storedMaxValue = getMaxHelper(Account.class);

        assertThat(storedMaxValue).isEqualTo(givenAccount);
    }

    @Test
    @Order(25)
    @SneakyThrows
    @DisplayName("Overloaded put returns provided value when the current max is greater")
    void overloadedPutReturnsProvidedValueWhenCurrentMaxIsGreater() {
        var givenAccount = Accounts.generateAccount();
        givenMaxHolderWithData(Account.class, givenAccount);
        var smallerBalanceAccount = Accounts.generateAccount();
        smallerBalanceAccount.setBalance(givenAccount.getBalance().subtract(BigDecimal.TEN));

        var returnedValue = callPut(Account.class, smallerBalanceAccount, Comparator.comparing(Account::getBalance));

        assertThat(returnedValue).isEqualTo(smallerBalanceAccount);
    }

    @Test
    @Order(26)
    @DisplayName("getMax method exists")
    void getMaxExists() {
        var getMaxMethodExists = Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())
                .anyMatch(m -> m.getName().equals("getMax"));

        assertThat(getMaxMethodExists).isTrue();
    }

    @Test
    @Order(27)
    @DisplayName("getMax declares one simple type param 'T'")
    void getMaxDeclaresOneTypeParam() {
        var getMaxMethod = getGetMaxMethod();

        var typeParams = getMaxMethod.getTypeParameters();

        assertThat(typeParams).hasSize(1);
        assertThat(typeParams[0].getTypeName()).isEqualTo("T");
    }

    @Test
    @Order(28)
    @DisplayName("getMax has one parameter")
    void getMaxHasOneParameter() {
        var getMaxMethod = getGetMaxMethod();

        var parameters = getMaxMethod.getParameters();

        assertThat(parameters).hasSize(1);
        assertThat(parameters[0].getType()).isEqualTo(Class.class);
    }

    @Test
    @Order(29)
    @DisplayName("getMax param specifies type arguments")
    void getMaxParamSpecifyTypeArguments() {
        var getMaxMethod = getGetMaxMethod();

        var parameter = getMaxMethod.getParameters()[0];

        assertThat(parameter.getParameterizedType().getTypeName()).isEqualTo(Class.class.getTypeName() + "<T>");
    }

    @Test
    @Order(30)
    @DisplayName("getMax returns value when it exists")
    void getMaxReturnsValueWhenItExists() {
        givenMaxHolderWithData(String.class, "I am maximum");

        var returnedValue = callGetMax(String.class);

        assertThat(returnedValue).isEqualTo("I am maximum");
    }

    @Test
    @Order(31)
    @DisplayName("getMax returns value when it exists")
    void getMaxReturnsNullWhenNoValueByGivenTypeExists() {
        var returnedValue = callGetMax(String.class);

        assertThat(returnedValue).isNull();
    }

    @Test
    @Order(32)
    @DisplayName("HeterogeneousMaxHolder keeps track of value one per each type")
    void maxHolderKeepsTrackOfMultipleValuesPerType() {
        callPut(String.class, "A");
        callPut(String.class, "C");
        callPut(String.class, "B");
        callPut(Long.class, 1L);
        callPut(Long.class, 5L);
        callPut(Long.class, 25L);

        var stringMax = callGetMax(String.class);
        var longMax = callGetMax(Long.class);

        assertThat(stringMax).isEqualTo("C");
        assertThat(longMax).isEqualTo(25L);
    }

    @SneakyThrows
    private <T> T callGetMax(Class<T> type) {
        var getMaxMethod = getGetMaxMethod();
        var result = getMaxMethod.invoke(heterogeneousMaxHolder, type);
        return type.cast(result);
    }

    private Method getGetMaxMethod() {
        return Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())
                .filter(m -> m.getName().equals("getMax"))
                .findAny()
                .orElseThrow();
    }

    private Method getPutMethod() {
        return Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())
                .filter(m -> m.getName().equals("put"))
                .filter(m -> m.getParameters().length <= 2)
                .findAny()
                .orElseThrow();
    }

    private Method getOverloadedPutMethod() {
        return Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())
                .filter(m -> m.getName().equals("put"))
                .filter(m -> m.getParameters().length == 3)
                .findAny()
                .orElseThrow();
    }

    private void givenMaxHolderWithData(Class<?> type, Object value) {
        var map = getInternalMap();
        map.put(type, value);
        assertThat(getMaxHelper(type)).isEqualTo(value);
    }

    @SneakyThrows
    private <T> T callPut(Class<T> type, T value) {
        var putMethod = HeterogeneousMaxHolder.class.getMethod("put", Class.class, Comparable.class);

        return type.cast(putMethod.invoke(heterogeneousMaxHolder, type, value));
    }

    @SneakyThrows
    private <T> T callPut(Class<T> type, T value, Comparator<T> comparator) {
        var putMethod = HeterogeneousMaxHolder.class.getMethod("put", Class.class, Object.class, Comparator.class);

        return type.cast(putMethod.invoke(heterogeneousMaxHolder, type, value, comparator));
    }

    @SneakyThrows
    private <T> T getMaxHelper(Class<T> type) {
        Map<Class<?>, Object> map = getInternalMap();
        return type.cast(map.get(type));
    }

    @SneakyThrows
    private Map<Class<?>, Object> getInternalMap() {
        var mapField = HeterogeneousMaxHolder.class.getDeclaredFields()[0];
        mapField.setAccessible(true);
        @SuppressWarnings("unchecked")
        var map = (Map<Class<?>, Object>) mapField.get(heterogeneousMaxHolder);
        return map;
    }
}

================================================
FILE: 1-0-java-basics/1-5-0-hello-annotations/README.MD
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Hello Annotations
Learn annotations basics to better understand how the frameworks use them 💪

### Objectives

* **create a custom annotation** ✅
* specify **where it can be used** ([`@Target`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Target.html)) ✅
* 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)) ✅
* add **annotation members** (methods that act like fields) ✅
* set **default value** for a member ✅
* **use custom annotation** on a class ✅


### Exercise overview 🇺🇦
[![Watch the video](https://img.youtube.com/vi/KF1H2EOCdD4/0.jpg)](https://www.youtube.com/watch?v=KF1H2EOCdD4)

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)
##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 1-0-java-basics/1-5-0-hello-annotations/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>1-0-java-basics</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>1-5-0-hello-annotations</artifactId>

</project>

================================================
FILE: 1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java
================================================
package com.bobocode.basics;

/**
 * {@link HelloAnnotationsExercise} is an exercise class that is marked with be corresponding @{@link Exercise}
 * annotation. The annotation value specifies exercise name "hello-annotation-basic". It does not specify any custom
 * complexity level, because this exercise is a basic, which correspond to the default value provided by annotation.
 * <p>
 * todo: Create an annotation @{@link Exercise}.
 * todo: Set its retention policy so it is visible at runtime
 * todo: Set its target so it can only be applied to a class
 * todo: Add String value that will store exercise name
 * todo: Add complexityLevel with a default {@link Level} basic
 *
 * @author Taras Boychuk
 */
public class HelloAnnotationsExercise { // todo: mark class with the annotation according to the javadoc
}


================================================
FILE: 1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/Level.java
================================================
package com.bobocode.basics;

/**
 * Enum that lists all possible exercise complexity levels.
 */
public enum Level {
    BEGINNER, BASIC, ADVANCED, CRAZY
}


================================================
FILE: 1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsExerciseTest.java
================================================
package com.bobocode.basics;


import lombok.SneakyThrows;
import org.junit.jupiter.api.*;

import java.lang.annotation.*;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class HelloAnnotationsExerciseTest {

    @Test
    @Order(1)
    @DisplayName("Annotation @Exercise exists")
    void exerciseAnnotationExists() {
        assertThatCode(this::getExerciseAnnotation).doesNotThrowAnyException();
    }

    @Test
    @Order(2)
    @DisplayName("@Exercise can be applied to classes and interfaces but not to methods & fields")
    @SneakyThrows
    void exerciseAnnotationCanBeAppliedForClasses() {
        var exerciseAnnotation = getExerciseAnnotation();

        var target = exerciseAnnotation.getAnnotation(Target.class);

        assertThat(target.value()).hasSize(1);
        assertThat(target.value()[0]).isEqualTo(ElementType.TYPE);
    }

    @Test
    @Order(3)
    @DisplayName("@Exercise information is accessible at runtime")
    @SneakyThrows
    void exerciseAnnotationInfoIsAccessibleAtRuntime() {
        var exerciseAnnotation = getExerciseAnnotation();

        var retention = exerciseAnnotation.getAnnotation(Retention.class);

        assertThat(retention.value()).isEqualTo(RetentionPolicy.RUNTIME);
    }

    @Test
    @Order(4)
    @DisplayName("@Exercise has declared value")
    @SneakyThrows
    void exerciseAnnotationHasValueDeclared() {
        var exerciseAnnotation = getExerciseAnnotation();

        assertThatCode(() -> exerciseAnnotation.getDeclaredMethod("value"))
                .doesNotThrowAnyException();
    }

    @Test
    @Order(4)
    @DisplayName("@Exercise has complexityLevel declared")
    @SneakyThrows
    void exerciseAnnotationHasComplexityDeclared() {
        var exerciseAnnotation = getExerciseAnnotation();

        assertThatCode(() -> exerciseAnnotation.getDeclaredMethod("complexityLevel"))
                .doesNotThrowAnyException();
    }

    @Test
    @Order(5)
    @DisplayName("@Exercise complexityLevel is BASIC by default")
    @SneakyThrows
    void exerciseAnnotationComplexityLevelDefaultValue() {
        var exerciseAnnotation = getExerciseAnnotation();

        var complexityLevel = exerciseAnnotation.getDeclaredMethod("complexityLevel");

        assertThat(complexityLevel.getDefaultValue()).isEqualTo(Level.BASIC);
    }

    @Test
    @Order(6)
    @DisplayName("HelloAnnotationExercise is marked as @Exercise with name \"hello-annotation-basic\"")
    @SneakyThrows
    void helloAnnotationExerciseIsAnnotatedWithExercise() {
        var exerciseAnnotationClass = getExerciseAnnotation();
        var basicExerciseAnnotation = HelloAnnotationsExercise.class.getAnnotation(exerciseAnnotationClass);

        var valueMethod = exerciseAnnotationClass.getMethod("value");
        var exerciseName = valueMethod.invoke(basicExerciseAnnotation);

        assertThat(exerciseName).isEqualTo("hello-annotation-basic");
    }

    @SneakyThrows
    private Class<? extends Annotation> getExerciseAnnotation() {
        return Class.forName("com.bobocode.basics.Exercise")
                .asSubclass(Annotation.class);
    }

}


================================================
FILE: 1-0-java-basics/README.md
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Java Basics
This module intentionally left empty and will be populated with new material in future

================================================
FILE: 1-0-java-basics/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>1-3-0-hello-generics</module>
        <module>1-3-1-crazy-generics</module>
        <module>1-3-2-heterogeneous-max-holder</module>
        <module>1-5-0-hello-annotations</module>
    </modules>

    <parent>
        <groupId>com.bobocode</groupId>
        <artifactId>java-fundamentals-exercises</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>1-0-java-basics</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.bobocode</groupId>
            <artifactId>java-fundamentals-util</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

</project>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-1-node/README.MD
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Node exercise 💪

Build strong skill of creating and linking **nodes – building blocks** that are used in order to create **LinkedList**, **LinkedQueue** and other
important data structures 💪

### Pre-conditions ❗
You're supposed to be familiar **Java classes** and **generics**

### Objectives
* implement a generic class `Node<T>` ✅
* **link** two node objects ✅
* create a **list of linked nodes** ✅
* create a **circle of linked nodes** ✅

### Exercise overview 🇺🇦
[![Watch the video](https://img.youtube.com/vi/Ot5ma8NXcS0/0.jpg)](https://www.youtube.com/watch?v=Ot5ma8NXcS0)

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-1-node/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>2-0-data-structures-and-algorithms</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>2-2-1-node</artifactId>


</project>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-1-node/src/main/java/com/bobobode/cs/Node.java
================================================
package com.bobobode.cs;

/**
 * Class {@link Node} is a very simple data structure that consists of an element itself and the reference to the next
 * node. An element can have any value since it's a generic. A reference to the next node allows to link {@link Node}
 * objects and build more comprehensive data structures on top of those liked nodes.
 *
 * @param <T> a generic type T
 * @author Taras Boychuk
 */
public class Node<T> {
    // todo:
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-1-node/src/main/java/com/bobobode/cs/Nodes.java
================================================
package com.bobobode.cs;

import com.bobocode.util.ExerciseNotCompletedException;

/**
 * A class that consists of static methods only and provides util methods for {@link Node}.
 * <p><p>
 * <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com">visit our website</a></strong>
 * <p>
 *
 * @author Taras Boychuk
 */
public class Nodes {
    private Nodes() {
    }

    /**
     * Creates a new instance of {@link Node} that holds provided element
     *
     * @param element any element of type T
     * @param <T>     generic type
     * @return a new instance of {@link Node}
     */
    public static <T> Node<T> create(T element) {
        throw new ExerciseNotCompletedException(); // todo:
    }

    /**
     * Create a connection between first and second nodes, so object first stores a reference to the second.
     *
     * @param first  any {@link Node} object
     * @param second any {@link Node} object
     * @param <T>    a genetic type
     */
    public static <T> void link(Node<T> first, Node<T> second) {
        throw new ExerciseNotCompletedException(); // todo:
    }

    /**
     * Creates two new {@link Node} objects using provided firstElement and secondElement, and create a connection
     * between those two elements so the first node will hold a reference to a second one.
     *
     * @param firstElement  any element of type T
     * @param secondElement any element of type T
     * @param <T>           a genetic type
     * @return a reference to a first node created based on firstElement
     */
    public static <T> Node<T> pairOf(T firstElement, T secondElement) {
        throw new ExerciseNotCompletedException(); // todo:
    }

    /**
     * Creates two new {@link Node} objects using provided firstElement and secondElement, and creates connections
     * between those nodes so the first node will hold a reference to a second one, and a second node will hold
     * a reference to the first one.
     *
     * @param firstElement  any element of type T
     * @param secondElement any element of type T
     * @param <T>           generic type T
     * @return a reference to the first node
     */
    public static <T> Node<T> closedPairOf(T firstElement, T secondElement) {
        throw new ExerciseNotCompletedException(); // todo:
    }

    /**
     * Creates a linked chain of {@link Node} objects based on provided elements. Creates a connection between those
     * nodes so each node will hold a reference to the next one in the chain. HINT: it's basically a linked list.
     *
     * @param elements a array of elements of type T
     * @param <T>      generic type T
     * @return a reference to the first element of the chain
     */
    public static <T> Node<T> chainOf(T... elements) {
        throw new ExerciseNotCompletedException(); // todo:
    }

    /**
     * Creates a linked circle of {@link Node} objects based on provided elements. Creates a connection between those
     * nodes so each node will hold a reference to the next one in the chain, and the last one will hold a reference to
     * the first one.
     *
     * @param elements a array of elements of type T
     * @param <T>      generic type T
     * @return a reference to the first element of the chain
     */
    public static <T> Node<T> circleOf(T... elements) {
        throw new ExerciseNotCompletedException(); // todo:
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-1-node/src/test/java/com/bobocode/cs/NodesTest.java
================================================
package com.bobocode.cs;

import com.bobobode.cs.Node;
import com.bobobode.cs.Nodes;
import lombok.SneakyThrows;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class NodesTest {

    @Test
    @Order(1)
    void create() {
        int element = 5;

        Node<Integer> node = Nodes.create(element);

        assertThat(getNodeElement(node)).isEqualTo(element);
        assertThat(getNodeNext(node)).isNull();
    }

    @Test
    @Order(2)
    void link() {
        Node<Integer> firstNode = createNodeOf(5);
        Node<Integer> secondNode = createNodeOf(9);
        Node<Integer> thirdNode = createNodeOf(100);
        setNodeNext(secondNode, thirdNode);

        Nodes.link(firstNode, secondNode);

        assertThat(getNodeNext(firstNode)).isEqualTo(secondNode);
        assertThat(getNodeNext(secondNode)).isEqualTo(thirdNode);
    }

    @Test
    @Order(3)
    void pair() {
        int firstElement = 8;
        int secondElement = 2;

        Node<Integer> firstNode = Nodes.pairOf(firstElement, secondElement);

        Node<Integer> secondNode = getNodeNext(firstNode);
        assertThat(getNodeElement(firstNode)).isEqualTo(firstElement);
        assertThat(getNodeElement(secondNode)).isEqualTo(secondElement);
        assertThat(getNodeNext(secondNode)).isNull();
    }

    @Test
    @Order(4)
    void closedPair() {
        int firstElement = 8;
        int secondElement = 2;

        Node<Integer> firstNode = Nodes.closedPairOf(firstElement, secondElement);

        Node<Integer> secondNode = getNodeNext(firstNode);
        assertThat(getNodeElement(firstNode)).isEqualTo(firstElement);
        assertThat(getNodeElement(secondNode)).isEqualTo(secondElement);
        assertThat(getNodeNext(secondNode)).isEqualTo(firstNode);
    }

    @Test
    @Order(5)
    void chain() {
        int firstElement = 8;
        int secondElement = 1;
        int thirdElement = 13;
        int fourthElement = 5;

        Node<Integer> firstNode = Nodes.chainOf(firstElement, secondElement, thirdElement, fourthElement);

        Node<Integer> secondNode = getNodeNext(firstNode);
        Node<Integer> thirdNode = getNodeNext(secondNode);
        Node<Integer> fourthNode = getNodeNext(thirdNode);
        assertThat(getNodeElement(firstNode)).isEqualTo(firstElement);
        assertThat(getNodeElement(secondNode)).isEqualTo(secondElement);
        assertThat(getNodeElement(thirdNode)).isEqualTo(thirdElement);
        assertThat(getNodeElement(fourthNode)).isEqualTo(fourthElement);
        assertThat(getNodeNext(fourthNode)).isNull();
    }

    @Test
    @Order(6)
    void circle() {
        int firstElement = 8;
        int secondElement = 1;
        int thirdElement = 13;
        int fourthElement = 5;

        Node<Integer> firstNode = Nodes.circleOf(firstElement, secondElement, thirdElement, fourthElement);

        Node<Integer> secondNode = getNodeNext(firstNode);
        Node<Integer> thirdNode = getNodeNext(secondNode);
        Node<Integer> fourthNode = getNodeNext(thirdNode);
        assertThat(getNodeElement(firstNode)).isEqualTo(firstElement);
        assertThat(getNodeElement(secondNode)).isEqualTo(secondElement);
        assertThat(getNodeElement(thirdNode)).isEqualTo(thirdElement);
        assertThat(getNodeElement(fourthNode)).isEqualTo(fourthElement);
        assertThat(getNodeNext(fourthNode)).isEqualTo(firstNode);
    }

    @SneakyThrows
    @SuppressWarnings("unchecked")
    private Node<Integer> createNodeOf(int element) {
        Constructor<?> constructor = Arrays.stream(Node.class.getDeclaredConstructors())
                .findAny()
                .orElseThrow();
        constructor.setAccessible(true);
        Node<Integer> node;
        if (constructor.getParameters().length > 0) {
            node = (Node<Integer>) constructor.newInstance(element);
        } else {
            node = (Node<Integer>) constructor.newInstance();
            setNodeElement(node, element);
        }
        return node;
    }

    @SneakyThrows
    @SuppressWarnings("unchecked")
    private <T> T getNodeElement(Node<T> node) {
        Field elementField = getAccessibleElementField();
        return (T) elementField.get(node);
    }

    @SneakyThrows
    private <T> void setNodeElement(Node<T> node, T element) {
        Field elementField = getAccessibleElementField();
        elementField.set(node, element);
    }

    @SneakyThrows
    @SuppressWarnings("unchecked")
    private <T> Node<T> getNodeNext(Node<T> node) {
        Field nextField = getAccessibleNextField();
        return (Node<T>) nextField.get(node);
    }

    @SneakyThrows
    private <T> void setNodeNext(Node<T> node, Node<T> next) {
        Field elementField = getAccessibleNextField();
        elementField.set(node, next);
    }

    private Field getAccessibleElementField() {
        Field elementField = Arrays.stream(Node.class.getDeclaredFields())
                .filter(field -> field.getType().equals(Object.class))
                .findAny()
                .orElseThrow();
        elementField.setAccessible(true);
        return elementField;
    }

    private Field getAccessibleNextField() {
        Field nextField = Arrays.stream(Node.class.getDeclaredFields())
                .filter(field -> field.getType().equals(Node.class))
                .findAny()
                .orElseThrow();
        nextField.setAccessible(true);
        return nextField;
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/README.md
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Stack 🥞
Learn the Stack data structure and gain deep understanding implementing it on your own 💪


### WHY ❓
[Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) is one of the most important data structures 
for software developers. It can be used in various algorithms, but the most important is that **JVM creates a stack 
for each thread.** 😯 Even if you don't know anything about concurrency, and you write a simple application, it is still
**executed by one main thread.** So each thread has its own stack, and **that stack is used to store method frames.** 😲
A [method frame](https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-2.html#jvms-2.6) is just a term. It simply
means all the information we operate with, when executing a method. E.g. **when you pass some arguments, or create 
local variable, all that data is stored to the stack.** So, **every a method is called, a new stack frame is created and
stored to the stack.** 

> 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**.

### Objectives
* implement a generic class `Node<T>` ✅
* **push an element onto the stack** ✅
* **get an element from the stack** ✅
* maintain stack **size** ✅

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction) 

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>2-0-data-structures-and-algorithms</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>2-2-2-stack</artifactId>


</project>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/LinkedStack.java
================================================
package com.bobocode.cs;

import com.bobocode.cs.exception.EmptyStackException;
import com.bobocode.util.ExerciseNotCompletedException;

/**
 * {@link LinkedStack} is a stack implementation that is based on singly linked generic nodes.
 * A node is implemented as inner static class {@link Node<T>}.
 * <p><p>
 * <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com">visit our website</a></strong>
 * <p>
 *
 * @param <T> generic type parameter
 * @author Taras Boychuk
 * @author Serhii Hryhus
 */
public class LinkedStack<T> implements Stack<T> {

    /**
     * This method creates a stack of provided elements
     *
     * @param elements elements to add
     * @param <T>      generic type
     * @return a new stack of elements that were passed as method parameters
     */
    public static <T> LinkedStack<T> of(T... elements) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * The method pushes an element onto the top of this stack. This has exactly the same effect as:
     * addElement(item)
     *
     * @param element elements to add
     */
    @Override
    public void push(T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * This method removes the object at the top of this stack
     * and returns that object as the value of this function.
     *
     * @return The object at the top of this stack
     * @throws EmptyStackException - if this stack is empty
     */
    @Override
    public T pop() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Returns the number of elements in the stack
     *
     * @return number of elements
     */
    @Override
    public int size() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Checks if a stack is empty
     *
     * @return {@code true} if a stack is empty, {@code false} otherwise
     */
    @Override
    public boolean isEmpty() {
        throw new ExerciseNotCompletedException(); // todo: implement this method;
    }

}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/Stack.java
================================================
package com.bobocode.cs;

/**
 * {@link Stack} is a fundamental data structure that follows last-in-first-out (LIFO) principle. This interface
 * represents a simple contact, that can be implemented in various ways (e.g. using existing collections, arrays or
 * custom linked nodes)
 *
 * @param <T> type parameter
 * @author Taras Boychuk
 * @author Serhii Hryhus
 */
public interface Stack<T> {

    void push(T element);

    T pop();

    int size();

    boolean isEmpty();
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/exception/EmptyStackException.java
================================================
package com.bobocode.cs.exception;

public class EmptyStackException extends RuntimeException{

}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/src/test/java/com/bobocode/cs/LinkedStackTest.java
================================================
package com.bobocode.cs;

import com.bobocode.cs.exception.EmptyStackException;
import lombok.SneakyThrows;
import org.junit.jupiter.api.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.function.Predicate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.junit.jupiter.api.Assertions.assertThrows;

/**
 * A reflection-based test class for {@link LinkedStack}.
 * <p>
 * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.
 *
 * @author Ivan Virchenko
 * @author Taras Boychuk
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class LinkedStackTest {
    private static final String PROPER_CLASSNAME = "Node";

    private static final Predicate<Field> NODE_FIELD_PREDICATE = field ->
            field.getType().getSimpleName().equals(PROPER_CLASSNAME)
            && field.getName().toLowerCase().contains("head")
            || field.getName().toLowerCase().contains("first");

    private static final Predicate<Field> SIZE_FIELD_PREDICATE = field ->
            field.getName().toLowerCase().contains("size");

    private static final Predicate<Field> NODE_ELEMENT_FIELD_PREDICATE = field ->
            field.getName().toLowerCase().contains("element")
            || field.getName().toLowerCase().contains("value")
            || field.getName().toLowerCase().contains("item");

    private static final Predicate<Field> NODE_NEXT_FIELD_PREDICATE = field ->
            field.getType().getSimpleName().equals(PROPER_CLASSNAME)
            && field.getName().toLowerCase().contains("next");

    private Stack<Integer> intStack = new LinkedStack<>();

    @Test
    @Order(1)
    @DisplayName("Inner class Node is created")
    void checkProperInnerClassName() {
        String name = getInnerClass().getSimpleName();
        assertThat(name).isEqualTo(PROPER_CLASSNAME);
    }

    @Test
    @Order(2)
    @DisplayName("Class Node is a generic class")
    void noteIsAGenericClass() {
        var nodeTypeParams = getInnerClass().getTypeParameters();

        assertThat(nodeTypeParams).hasSize(1);
    }

    @Test
    @Order(3)
    @DisplayName("LinkedStack class has a field that stores a reference to the first(head) element")
    void checkProperHeadFieldName() {
        Field[] fields = LinkedStack.class.getDeclaredFields();

        boolean hasNodeField = Arrays.stream(fields)
                .anyMatch(NODE_FIELD_PREDICATE);

        assertThat(hasNodeField).isTrue();
    }

    @Test
    @Order(4)
    @DisplayName("LinkedStack class has a field to store stack size")
    void checkProperSizeFieldName() {
        Field[] fields = LinkedStack.class.getDeclaredFields();

        boolean hasSizeField = Arrays.stream(fields)
                .anyMatch(SIZE_FIELD_PREDICATE);

        assertThat(hasSizeField).isTrue();
    }

    @Test
    @Order(5)
    @DisplayName("Node class has a field to store a generic element")
    void checkProperElementField() {
        var fields = getInnerClass().getDeclaredFields();

        var elementField = Arrays.stream(fields)
                .filter(NODE_ELEMENT_FIELD_PREDICATE)
                .findAny()
                .orElseThrow();
        var nodeTypeParameter = getInnerClass().getTypeParameters()[0];


        assertThat(elementField.getGenericType().getTypeName()).isEqualTo(nodeTypeParameter.getTypeName());
    }

    @Test
    @Order(6)
    @DisplayName("Node class has a field to store a reference to the next node")
    void checkProperNextField() {
        Field[] fields = getInnerClass().getDeclaredFields();

        boolean hasNext = Arrays.stream(fields)
                .anyMatch(NODE_NEXT_FIELD_PREDICATE);

        assertThat(hasNext).isTrue();
    }

    @Test
    @Order(7)
    @DisplayName("Method of() creates a new LinkedStack of given elements")
    void of() {
        intStack = LinkedStack.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        for (int i = 1; i <= 10; i++) {
            assertThat(contains(i)).isTrue();
        }
    }

    @Test
    @Order(8)
    @DisplayName("Method push() adds new element on top of the stack")
    void push() {
        intStack.push(55);

        assertThat(contains(55)).isTrue();
    }

    @Test
    @Order(9)
    @DisplayName("Method push() adds new element on top of the stack")
    void pushAddsElementWhenStackIsEmpty() {
        intStack.push(243);

        assertThat(contains(243)).isTrue();
    }

    @Test
    @Order(10)
    @DisplayName("Method push() adds new element on top of the stack when it's empty")
    void pushAddsElementToHeadWhenStackIsEmpty() {
        intStack.push(10);

        Object head = getHeadObject();
        int innerElement = getNodeElementInt(head);

        assertThat(innerElement).isEqualTo(10);
    }

    @Test
    @Order(11)
    @DisplayName("Method push() adds new element on top of the stack when it's not empty")
    void pushAddsElementToHeadWhenStackIsNotEmpty() {
        intStack.push(10);
        intStack.push(15);
        intStack.push(20);

        Object head = getHeadObject();
        int innerElement = getNodeElementInt(head);

        assertThat(innerElement).isEqualTo(20);
    }

    @Test
    @Order(12)
    @DisplayName("Method push() links new element with the previous top (head) element")
    void pushPutsHeadToNextOfNewHead() {
        fillTestStack(10, 15, 20);

        assertThat(getNodeElementInt(getHeadObject())).isEqualTo(20);

        intStack.push(30);

        Object head = getHeadObject();
        Object next = getNodeNextObject(head);
        int nextElement = getNodeElementInt(next);

        assertThat(nextElement).isEqualTo(20);
    }

    @Test
    @Order(13)
    @DisplayName("Method push() throws exception when element is null")
    void pushThrowsExceptionWhenElementIsNull() {
        assertThatNullPointerException().isThrownBy(() -> intStack.push(null));
    }

    @Test
    @Order(14)
    @DisplayName("Method pop() throws exception when stack is empty")
    void popElementWhenStackIsEmpty() {
        assertThrows(EmptyStackException.class, () -> intStack.pop());
    }

    @Test
    @Order(15)
    @DisplayName("Method pop() retrieves top element from the stack (LIFO)")
    void pop() {
        fillTestStack(55, 17, 66, 234);

        int lastElement = intStack.pop();

        assertThat(lastElement).isEqualTo(234);
    }

    @Test
    @Order(16)
    @DisplayName("Method pop() assigns next element to be a head")
    void popResetsHeadFromNextOfOldHead() {
        fillTestStack(10, 15, 20);
        Object head = getHeadObject();

        assertThat(getNodeElementInt(head)).isEqualTo(20);

        intStack.pop();
        head = getHeadObject();

        assertThat(getNodeElementInt(head)).isEqualTo(15);
    }

    @Test
    @Order(17)
    @DisplayName("Method size() returns 0 when stack is empty")
    void sizeWhenStackIsEmpty() {
        int actualSize = getInnerSize();

        assertThat(actualSize).isEqualTo(0);
    }

    @Test
    @Order(18)
    @DisplayName("Method size() returns number of element in the stack")
    void size() {
        fillTestStack(1, 5, 7);

        assertThat(intStack.size()).isEqualTo(3);
    }

    @Test
    @Order(19)
    @DisplayName("Method size() returns correct value when stack was created via method of()")
    void sizeIncreasesWhenUseOfMethod() {
        intStack = LinkedStack.of(1, 2, 3, 4, 5, 6, 7, 8);

        assertThat(intStack.size()).isEqualTo(8);
    }

    @Test
    @Order(20)
    @DisplayName("Method size() correct value when elements were added via push()")
    void sizeIncreasesWhenPush() {
        intStack.push(1);
        intStack.push(2);
        intStack.push(3);

        assertThat(intStack.size()).isEqualTo(3);
    }

    @Test
    @Order(21)
    @DisplayName("Method size() correct value when elements were removed via pop()")
    void sizeDecreasesWhenPop() {
        fillTestStack(1, 2, 3, 4, 5);
        intStack.pop();

        assertThat(intStack.size()).isEqualTo(4);
    }

    @Test
    @Order(22)
    @DisplayName("Method isEmpty() returns true when stack contains elements")
    void isEmpty() {
        fillTestStack(87, 53, 66);

        boolean stackEmpty = intStack.isEmpty();

        assertThat(stackEmpty).isEqualTo(false);
    }

    @Test
    @Order(23)
    @DisplayName("Method isEmpty() returns false when stack contains no elements")
    void isEmptyWhenStackIsEmpty() {
        boolean stackEmpty = intStack.isEmpty();
        assertThat(stackEmpty).isEqualTo(true);
    }

    private Class<?> getInnerClass() {
        return Arrays.stream(LinkedStack.class.getDeclaredClasses())
                .filter(Class::isMemberClass)
                .findAny().orElseThrow();
    }

    private Field getHeadField() {
        Field headField = Arrays.stream(LinkedStack.class.getDeclaredFields())
                .filter(NODE_FIELD_PREDICATE)
                .findAny()
                .orElseThrow();
        headField.setAccessible(true);
        return headField;
    }

    private Field getNodeElementField(Object node) {
        Field fieldElement = Arrays.stream(node.getClass().getDeclaredFields())
                .filter(NODE_ELEMENT_FIELD_PREDICATE)
                .findAny()
                .orElseThrow();
        fieldElement.setAccessible(true);
        return fieldElement;
    }

    private Field getNodeNextField(Object node) {
        Field field = Arrays.stream(node.getClass().getDeclaredFields())
                .filter(NODE_NEXT_FIELD_PREDICATE)
                .findAny()
                .orElseThrow();
        field.setAccessible(true);
        return field;
    }

    @SneakyThrows
    private Object getHeadObject() {
        return getHeadField().get(intStack);
    }

    @SneakyThrows
    private int getNodeElementInt(Object node) {
        return (int) getNodeElementField(node).get(node);
    }

    @SneakyThrows
    private Object getNodeNextObject(Object node) {
        return getNodeNextField(node).get(node);
    }

    private boolean contains(int element) {
        Object head = getHeadObject();
        if (head == null) {
            return false;
        }

        if (getNodeNextObject(head) != null) {
            return checkNext(head, element);
        } else {
            return getNodeElementInt(head) == element;
        }
    }

    private boolean checkNext(Object node, int element) {
        if (getNodeElementInt(node) == element) {
            return true;
        } else {
            return checkNext(getNodeNextObject(node), element);
        }
    }

    @SneakyThrows
    private Object newNode(int element) {
        Constructor<?> constructor = getInnerClass().getDeclaredConstructors()[0];
        constructor.setAccessible(true);

        if (constructor.getParameters().length == 1) {
            return constructor.newInstance(element);
        } else if (constructor.getParameters().length == 2) {
            return constructor.newInstance(element, null);
        } else {
            Object node = constructor.newInstance();
            getNodeElementField(node).set(node, element);
            return node;
        }
    }

    private void fillTestStack(int... elements) {
        for (int element : elements) {
            addToStack(element);
        }
        setInnerSize(elements.length);
    }

    @SneakyThrows
    private void addToStack(int element) {
        Object newNode = newNode(element);
        if (getHeadObject() != null) {
            getNodeNextField(newNode).set(newNode, getHeadObject());
        }
        getHeadField().set(intStack, newNode);
    }

    private Field getInnerSizeField() {
        Field size = Arrays.stream(LinkedStack.class.getDeclaredFields())
                .filter(SIZE_FIELD_PREDICATE)
                .findAny()
                .orElseThrow();
        size.setAccessible(true);
        return size;
    }

    @SneakyThrows
    private void setInnerSize(int size) {
        getInnerSizeField().set(intStack, size);
    }

    @SneakyThrows
    private int getInnerSize() {
        return (int) getInnerSizeField().get(intStack);
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-3-linked-queue/README.MD
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Linked Queue
Learn the idea of a queue and build strong skills implementing a queue based on linked nodes 💪
 
### Pre-conditions ❗
You're supposed to be familiar [Queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) data structure and generics in Java

### Objectives
* implement a generic class `Node<T>` ✅
* **add an element** to the end of the queue ✅
* **retrieve an element** from the begging of the queue ** ✅
* maintain queue **size** ✅

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-3-linked-queue/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>2-0-data-structures-and-algorithms</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>2-2-3-linked-queue</artifactId>


</project>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/main/java/com/bobocode/cs/LinkedQueue.java
================================================
package com.bobocode.cs;

import com.bobocode.util.ExerciseNotCompletedException;

/**
 * {@link LinkedQueue} implements FIFO {@link Queue}, using singly linked nodes. Nodes are stores in instances of nested
 * class Node. In order to perform operations {@link LinkedQueue#add(Object)} and {@link LinkedQueue#poll()}
 * in a constant time, it keeps to references to the head and tail of the queue.
 * <p><p>
 * <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com">visit our website</a></strong>
 * <p>
 *
 * @param <T> a generic parameter
 * @author Taras Boychuk
 * @author Ivan Virchenko
 */
public class LinkedQueue<T> implements Queue<T> {

    /**
     * Adds an element to the end of the queue.
     *
     * @param element the element to add
     */
    public void add(T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Retrieves and removes queue head.
     *
     * @return an element that was retrieved from the head or null if queue is empty
     */
    public T poll() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Returns a size of the queue.
     *
     * @return an integer value that is a size of queue
     */
    public int size() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Checks if the queue is empty.
     *
     * @return {@code true} if the queue is empty, returns {@code false} if it's not
     */
    public boolean isEmpty() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/main/java/com/bobocode/cs/Queue.java
================================================
package com.bobocode.cs;

/**
 * Queue is a data structure that follows "first in, first out" rule (FIFO). Operations {@link Queue#add(Object)} and
 * {@link Queue#poll()} are performed in constant time O(1)
 */
public interface Queue<T> {
    /**
     * Adds an element to the end of the queue.
     *
     * @param element the element to add
     */
    void add(T element);

    /**
     * Retrieves and removes queue head.
     *
     * @return an element that was retrieved from the head or null if queue is empty
     */
    T poll();

    /**
     * Returns a size of the queue.
     *
     * @return an integer value that is a size of queue
     */
    int size();

    /**
     * Checks if the queue is empty.
     *
     * @return {@code true} if the queue is empty, returns {@code false} if it's not
     */
    boolean isEmpty();
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/test/java/com/bobocode/cs/LinkedQueueTest.java
================================================
package com.bobocode.cs;

import lombok.SneakyThrows;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.function.Predicate;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * A reflection-based test class for {@link LinkedQueue}.
 * <p>
 * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.
 *
 * @author Victor Kuzma
 * @author Taras Boychuk
 * @author Ivan Virchenko
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LinkedQueueTest {
    private static final String NODE_NAME = "Node";
    private static final String SIZE_NAME = "size";

    private static final Predicate<Field> NODE_FIELD = field ->
            field.getType().getSimpleName().equals(NODE_NAME) && (field.getName().contains("next"));

    private static final Predicate<Field> ELEMENT_FIELD = field ->
            field.getGenericType().getTypeName().equals("T")
            && (field.getName().contains("elem") || field.getName().contains("value") || field.getName().contains("item"));

    private static final Predicate<Field> NEXT_FIELD = field ->
            field.getGenericType().getTypeName().endsWith("Node<T>") && (field.getName().contains("next"));

    private static final Predicate<Field> SIZE_FIELD = field ->
            field.getType().getSimpleName().equals("int") && (field.getName().equals(SIZE_NAME));

    private static final Predicate<Field> HEAD_FIELD = field ->
            field.getType().getSimpleName().equals(NODE_NAME)
            && (field.getName().contains("head") || field.getName().contains("first"));

    private static final Predicate<Field> TAIL_FIELD = field ->
            field.getType().getSimpleName().equals(NODE_NAME)
            && (field.getName().contains("tail") || field.getName().contains("last"));

    private Queue<Integer> integerQueue = new LinkedQueue<>();

    @Test
    @Order(1)
    void createNodeClass() {
        Class<?> innerClass = getInnerStaticNodeClass();
        String name = innerClass.getSimpleName();

        assertThat(name).isEqualTo(NODE_NAME);
    }

    @Test
    @Order(2)
    void checkFieldsNameInNodeClass() {
        Class<?> innerClass = getInnerStaticNodeClass();
        boolean hasElementField = hasField(innerClass, ELEMENT_FIELD);
        boolean hasNodeField = hasField(innerClass, NODE_FIELD);

        assertThat(hasElementField).isTrue();
        assertThat(hasNodeField).isTrue();
    }

    @Test
    @Order(3)
    void checkFieldsInQueueCLass() {
        Class<?> baseClass = this.integerQueue.getClass();
        boolean hasHeadField = hasField(baseClass, HEAD_FIELD);
        boolean hasSizeFiled = hasField(baseClass, SIZE_FIELD);
        boolean hasTailFiled = hasField(baseClass, TAIL_FIELD);

        assertThat(hasHeadField).isTrue();
        assertThat(hasSizeFiled).isTrue();
        assertThat(hasTailFiled).isTrue();
    }

    @Test
    @Order(4)
    void addFillsQueueWhenItIsEmpty() {
        integerQueue.add(1);
        integerQueue.add(228);
        int size = getInternalSize();
        boolean isEmpty = isEmptyQueue();
        Integer firstElement = (Integer) pollElementFromQueue();
        Integer secondElement = (Integer) pollElementFromQueue();

        assertThat(size).isEqualTo(2);
        assertThat(isEmpty).isEqualTo(false);
        assertThat(firstElement).isEqualTo(1);
        assertThat(secondElement).isEqualTo(228);
    }

    @Test
    @Order(5)
    void addFillsQueueWhenItIsNotEmpty() {
        addIntElementToQueue(12);
        addIntElementToQueue(13);
        integerQueue.add(111);
        int size = getInternalSize();
        boolean isEmpty = isEmptyQueue();
        Integer firstElement = (Integer) pollElementFromQueue();
        Integer secondElement = (Integer) pollElementFromQueue();
        Integer tailValue = (Integer) getNodeValue(TAIL_FIELD);

        assertThat(size).isEqualTo(3);
        assertThat(isEmpty).isEqualTo(false);
        assertThat(firstElement).isEqualTo(12);
        assertThat(secondElement).isEqualTo(13);
        assertThat(tailValue).isEqualTo(111);
    }

    @Test
    @Order(6)
    void addIncreasesQueueSize() {
        integerQueue.add(1);
        integerQueue.add(2);
        int size = getInternalSize();

        assertThat(size).isEqualTo(2);
    }

    @Test
    @Order(7)
    void pollReturnsNullWhenQueueIsEmpty() {
        Integer firstElement = this.integerQueue.poll();

        assertThat(firstElement).isEqualTo(null);
    }

    @Test
    @Order(8)
    void pollDeletesElementFromHead() {
        addIntElementToQueue(11);
        addIntElementToQueue(111);
        Integer firstElement = this.integerQueue.poll();
        Integer secondElement = this.integerQueue.poll();
        boolean isEmpty = isEmptyQueue();

        assertThat(isEmpty).isEqualTo(true);
        assertThat(firstElement).isEqualTo(11);
        assertThat(secondElement).isEqualTo(111);
    }

    @Test
    @Order(9)
    void pollDecreasesQueueSize() {
        addIntElementToQueue(11);
        addIntElementToQueue(111);
        this.integerQueue.poll();
        int size = getInternalSize();

        assertThat(size).isEqualTo(1);
    }

    @Test
    @Order(10)
    void pollMakesSizeZeroWhenQueueHasSingleElement() {
        addIntElementToQueue(12);
        Integer element = this.integerQueue.poll();
        int size = getInternalSize();

        assertThat(size).isEqualTo(0);
        assertThat(element).isEqualTo(12);
    }

    @Test
    @SneakyThrows
    @Order(11)
    void pollMakesQueueEmptyWhenQueueHasSingleElement() {
        addIntElementToQueue(1);
        this.integerQueue.poll();
        boolean isEmpty = isEmptyQueue();

        Object tail = getAccessibleFieldByPredicate(integerQueue, TAIL_FIELD).get(integerQueue);
        Object head = getAccessibleFieldByPredicate(integerQueue, HEAD_FIELD).get(integerQueue);

        assertThat(isEmpty).isEqualTo(true);
        assertThat(tail).isNull();
        assertThat(head).isNull();
    }

    @Test
    @Order(12)
    void sizeReturnsZeroWhenQueueIsEmpty() {
        int size = this.integerQueue.size();

        assertThat(size).isEqualTo(0);
    }

    @Test
    @Order(13)
    void size() {
        addIntElementToQueue(1);
        int size = this.integerQueue.size();

        assertThat(size).isEqualTo(1);
    }

    @Test
    @Order(14)
    void isEmptyReturnsTrueWhenQueueIsEmpty() {
        boolean isEmpty = this.integerQueue.isEmpty();

        assertThat(isEmpty).isEqualTo(true);
    }

    @Test
    @Order(15)
    void isEmpty() {
        addIntElementToQueue(1);
        boolean isEmpty = integerQueue.isEmpty();

        assertThat(isEmpty).isEqualTo(false);
    }


    private Class<?> getInnerStaticNodeClass() {
        return Arrays.stream(integerQueue.getClass().getDeclaredClasses())
                .filter(aClass -> Modifier.isStatic(aClass.getModifiers()))
                .findAny()
                .orElseThrow();
    }

    private boolean hasField(Class<?> classToSearch, Predicate targetField) {
        return Arrays.stream(classToSearch.getDeclaredFields())
                .anyMatch(targetField);
    }

    @SneakyThrows
    private int getInternalSize() {
        return (int) getAccessibleFieldByPredicate(this.integerQueue, SIZE_FIELD)
                .get(this.integerQueue);
    }


    @SneakyThrows
    private Object pollElementFromQueue() {
        Object element;
        Object nextElement;
        Object tail;
        Object head = getAccessibleFieldByPredicate(this.integerQueue,
                HEAD_FIELD)
                .get(this.integerQueue);

        Integer size = (Integer) getAccessibleFieldByPredicate(this.integerQueue,
                SIZE_FIELD)
                .get(this.integerQueue);

        if (head != null) {
            element = getAccessibleFieldByPredicate(head, ELEMENT_FIELD)
                    .get(head);

            nextElement = getAccessibleFieldByPredicate(head, NODE_FIELD)
                    .get(head);

            head = nextElement;
            setHead(head);

            if (head == null) {
                tail = null;
                setTail(tail);
            }

            if (size != null) {
                int tmpInt = size;
                tmpInt--;
                setInternalSize(tmpInt);
            }

            return element;
        } else {
            return null;
        }
    }

    @SneakyThrows
    private void addIntElementToQueue(int value) {
        Object newNode = createNode(value);
        Object head = getAccessibleFieldByPredicate(this.integerQueue, HEAD_FIELD).get(this.integerQueue);
        Object tail = getAccessibleFieldByPredicate(this.integerQueue, TAIL_FIELD).get(this.integerQueue);
        Integer size = (Integer) getAccessibleFieldByPredicate(this.integerQueue, SIZE_FIELD).get(this.integerQueue);

        if (head == null) {
            setHead(newNode);
        } else {
            setNextNode(tail, newNode);
        }
        setTail(newNode);

        if (size == null) {
            setInternalSize(1);
        } else {
            int tmpInt = size;
            tmpInt++;
            setInternalSize(tmpInt);
        }
    }

    @SneakyThrows
    private Object createNode(int value) {
        Object nodeObject;
        Class<?> innerClass = getInnerStaticNodeClass();
        Constructor<?>[] declaredConstructors = innerClass.getDeclaredConstructors();
        Constructor<?> constructor = declaredConstructors[0];
        constructor.setAccessible(true);

        if (constructor.getParameterTypes().length == 1) {
            nodeObject = constructor.newInstance(value);
        } else {
            nodeObject = constructor.newInstance();
            Field nodeElement = getAccessibleFieldByPredicate(nodeObject, ELEMENT_FIELD);
            nodeElement.set(nodeObject, value);
        }

        return nodeObject;
    }

    @SneakyThrows
    private boolean isEmptyQueue() {
        Object head = getAccessibleFieldByPredicate(this.integerQueue,
                HEAD_FIELD)
                .get(this.integerQueue);
        return head == null;
    }

    @SneakyThrows
    private void setInternalSize(int size) {
        Field sizeField = getAccessibleFieldByPredicate(this.integerQueue,
                SIZE_FIELD);
        sizeField.setInt(this.integerQueue, size);
    }

    @SneakyThrows
    private void setHead(Object obj) {
        Field head = getAccessibleFieldByPredicate(this.integerQueue, HEAD_FIELD);
        head.set(this.integerQueue, obj);
    }

    @SneakyThrows
    private void setTail(Object obj) {
        Field tail = getAccessibleFieldByPredicate(this.integerQueue, TAIL_FIELD);
        tail.set(this.integerQueue, obj);
    }

    @SneakyThrows
    private void setNextNode(Object current, Object next) {
        Field nodeNextField = getAccessibleFieldByPredicate(current, NEXT_FIELD);
        nodeNextField.set(current, next);
    }

    private Field getAccessibleFieldByPredicate(Object object, Predicate<Field> predicate) {
        Field field = Arrays.stream(object.getClass().getDeclaredFields())
                .filter(predicate)
                .findAny()
                .orElseThrow();
        field.setAccessible(true);
        return field;
    }

    @SneakyThrows
    private Object getNodeValue(Predicate<Field> predicate) {
        Object field = getAccessibleFieldByPredicate(integerQueue, predicate).get(integerQueue);
        final Field value = getAccessibleFieldByPredicate(field, ELEMENT_FIELD);
        value.setAccessible(true);
        return value.get(field);
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-4-linked-list/README.MD
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Linked List
Build strong skills implementing a well known Linked List data structure 💪

### Pre-conditions ❗
You're supposed to be familiar [Linked List](https://en.wikipedia.org/wiki/Linked_list) data structure and generics in Java

### Objectives
* implement a generic class `Node<T>` ✅
* **add an element** to the end of the list in **O(1)** ✅
* **add an element by index** (relink nodes when adding a new one inside the chain) ✅
* **set element** by index (find the correct node by index starting from `head`) ✅
* **remove element** by index (link prev and next nodes to get rid of the one that should be removed) ✅
* maintain list **size** ✅

### Exercise overview 🇺🇦
[![Watch the video](https://img.youtube.com/vi/knhSNO3bAHo/0.jpg)](https://www.youtube.com/watch?v=knhSNO3bAHo)

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-4-linked-list/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>2-0-data-structures-and-algorithms</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>2-2-4-linked-list</artifactId>

    
    <dependencies>
        <dependency>
            <groupId>com.bobocode</groupId>
            <artifactId>data-structures-and-algorithms-util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-4-linked-list/src/main/java/com/bobocode/cs/LinkedList.java
================================================
package com.bobocode.cs;


import com.bobocode.util.ExerciseNotCompletedException;

/**
 * {@link LinkedList} is a list implementation that is based on singly linked generic nodes. A node is implemented as
 * inner static class {@link Node<T>}.
 * <p><p>
 * <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com">visit our website</a></strong>
 * <p>
 *
 * @param <T> generic type parameter
 * @author Taras Boychuk
 * @author Serhii Hryhus
 */
public class LinkedList<T> implements List<T> {

    /**
     * This method creates a list of provided elements
     *
     * @param elements elements to add
     * @param <T>      generic type
     * @return a new list of elements the were passed as method parameters
     */
    public static <T> LinkedList<T> of(T... elements) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Adds an element to the end of the list.
     *
     * @param element element to add
     */
    @Override
    public void add(T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Adds a new element to the specific position in the list. In case provided index in out of the list bounds it
     * throws {@link IndexOutOfBoundsException}
     *
     * @param index   an index of new element
     * @param element element to add
     */
    @Override
    public void add(int index, T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Changes the value of an list element at specific position. In case provided index in out of the list bounds it
     * throws {@link IndexOutOfBoundsException}
     *
     * @param index   an position of element to change
     * @param element a new element value
     */
    @Override
    public void set(int index, T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Retrieves an elements by its position index. In case provided index in out of the list bounds it
     * throws {@link IndexOutOfBoundsException}
     *
     * @param index element index
     * @return an element value
     */
    @Override
    public T get(int index) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Returns the first element of the list. Operation is performed in constant time O(1)
     *
     * @return the first element of the list
     * @throws java.util.NoSuchElementException if list is empty
     */
    @Override
    public T getFirst() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Returns the last element of the list. Operation is performed in constant time O(1)
     *
     * @return the last element of the list
     * @throws java.util.NoSuchElementException if list is empty
     */
    @Override
    public T getLast() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Removes an elements by its position index. In case provided index in out of the list bounds it
     * throws {@link IndexOutOfBoundsException}
     *
     * @param index element index
     * @return deleted element
     */
    @Override
    public T remove(int index) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }


    /**
     * Checks if a specific exists in he list
     *
     * @return {@code true} if element exist, {@code false} otherwise
     */
    @Override
    public boolean contains(T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Checks if a list is empty
     *
     * @return {@code true} if list is empty, {@code false} otherwise
     */
    @Override
    public boolean isEmpty() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Returns the number of elements in the list
     *
     * @return number of elements
     */
    @Override
    public int size() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Removes all list elements
     */
    @Override
    public void clear() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java
================================================
package com.bobocode.cs;


import lombok.SneakyThrows;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.function.Predicate;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;

/**
 * A reflection-based test class for {@link LinkedList}.
 * <p>
 * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.
 *
 * @author Serhii Hryhus
 * @author Taras Boychuk
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LinkedListTest {
    private static final Predicate<Field> NODE_FIELD = field ->
            field.getType().getSimpleName().equals("Node");

    private static final Predicate<Field> HEAD_NODE_FIELD = field ->
            field.getType().getSimpleName().equals("Node")
            && (field.getName().contains("head") || field.getName().contains("first"));

    private static final Predicate<Field> TAIL_NODE_FIELD = field ->
            field.getType().getSimpleName().equals("Node")
            && (field.getName().equals("tail") || field.getName().contains("last"));

    private static final Predicate<Field> SIZE_FIELD = field ->
            field.getName().equals("size");

    private static final Predicate<Field> ELEMENT_FIELD = field ->
            field.getGenericType().getTypeName().equals("T")
            && (field.getName().contains("elem") || field.getName().contains("value") || field.getName().contains("item"));

    private LinkedList<Integer> intList = new LinkedList<>();

    @Test
    @Order(1)
    void properNodeName() {
        Class<?> innerClass = getInnerClass();

        String name = innerClass.getSimpleName();

        assertThat(name).isEqualTo("Node");
    }

    @Test
    @Order(2)
    void properNodeFields() {
        Class<?> innerClass = getInnerClass();

        boolean hasElementField = Arrays.stream(innerClass.getDeclaredFields())
                .anyMatch(ELEMENT_FIELD);

        boolean hasNodeField = Arrays.stream(innerClass.getDeclaredFields())
                .anyMatch(NODE_FIELD);

        assertThat(hasElementField).isTrue();
        assertThat(hasNodeField).isTrue();
    }

    private Class<?> getInnerClass() {
        return Arrays.stream(intList.getClass().getDeclaredClasses())
                .filter(aClass -> Modifier.isStatic(aClass.getModifiers()))
                .findAny()
                .orElseThrow();
    }

    @Test
    @Order(3)
    void addIntoEmptyList() {
        intList.add(41);

        int element = getInternalElement(0);

        assertThat(element).isEqualTo(41);
    }

    @Test
    @Order(4)
    void addIntoEmptyListChangesSize() {
        intList.add(41);

        int size = getInternalSize();

        assertThat(size).isEqualTo(1);
    }

    @Test
    @Order(5)
    void add() {
        intList.add(41);
        intList.add(30);
        intList.add(75);

        int firstElement = getInternalElement(0);
        int secondElement = getInternalElement(1);
        int thirdElement = getInternalElement(2);

        assertThat(firstElement).isEqualTo(41);
        assertThat(secondElement).isEqualTo(30);
        assertThat(thirdElement).isEqualTo(75);
    }

    @Test
    @Order(6)
    void addChangesSize() {
        intList.add(41);
        intList.add(30);
        intList.add(75);

        int size = getInternalSize();

        assertThat(size).isEqualTo(3);
    }

    @Test
    @Order(7)
    void addByIndex() {
        addInternalElements(43, 5, 6, 8);

        int newElementIdx = 2;
        intList.add(newElementIdx, 66);

        int elementByNewElementIndex = getInternalElement(newElementIdx);
        int size = getInternalSize();

        assertThat(elementByNewElementIndex).isEqualTo(66);
        assertThat(getInternalElement(0)).isEqualTo(43);
        assertThat(getInternalElement(1)).isEqualTo(5);
        assertThat(getInternalElement(3)).isEqualTo(6);
        assertThat(getInternalElement(4)).isEqualTo(8);
        assertThat(size).isEqualTo(5);
    }

    @Test
    @Order(8)
    void addByZeroIndexWhenListIsEmpty() {
        intList.add(0, 45);

        int element = getInternalElement(0);
        int size = getInternalSize();

        assertThat(element).isEqualTo(45);
        assertThat(size).isEqualTo(1);
    }

    @Test
    @Order(9)
    void addByIndexToTheEndOfList() {
        addInternalElements(98, 64, 23, 1, 3, 4);

        int newElementIndex = getInternalSize();
        intList.add(newElementIndex, 44);

        assertThat(getInternalElement(newElementIndex)).isEqualTo(44);
        assertThat(getInternalSize()).isEqualTo(7);
    }

    @Test
    @Order(10)
    void addToHeadWhenListIsNotEmpty() {
        addInternalElements(4, 6, 8, 9, 0, 2);

        intList.add(0, 53);

        int firstElement = getInternalElement(0);
        int secondElement = getInternalElement(1);
        int size = getInternalSize();

        assertThat(firstElement).isEqualTo(53);
        assertThat(secondElement).isEqualTo(4);
        assertThat(size).isEqualTo(7);
    }

    @Test
    @Order(11)
    void addThrowsExceptionWhenIndexIsNegative() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.add(-1, 66));
    }

    @Test
    @Order(12)
    void addThrowsExceptionWhenIndexLargerThanSize() {
        addInternalElements(4, 6, 11, 9);

        int newElementIdx = 5;

        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.add(newElementIdx, 88));
    }

    @Test
    @Order(13)
    void addWhenIndexEqualToSize() {
        addInternalElements(1, 2, 3, 4, 5); // size = 5

        intList.add(5, 111);

        int element = getInternalElement(5);
        int size = getInternalSize();

        assertThat(element).isEqualTo(111);
        assertThat(size).isEqualTo(6);
    }

    @Test
    @Order(14)
    void of() {
        intList = LinkedList.of(43, 233, 54);

        assertThat(getInternalElement(0)).isEqualTo(43);
        assertThat(getInternalElement(1)).isEqualTo(233);
        assertThat(getInternalElement(2)).isEqualTo(54);
    }

    @Test
    @Order(15)
    void ofChangeSize() {
        intList = LinkedList.of(43, 233, 54);

        int size = getInternalSize();

        assertThat(size).isEqualTo(3);
    }

    @Test
    @Order(16)
    void setByIndex() {
        addInternalElements(34, 78, 9, 8);

        int index = 2; //element = 78
        intList.set(index, 99);

        int elementOnNewElementIndex = getInternalElement(index);
        int nextElementToNewElementIndex = getInternalElement(3);
        int internalSize = getInternalSize();

        assertThat(elementOnNewElementIndex).isEqualTo(99);
        assertThat(nextElementToNewElementIndex).isEqualTo(8);
        assertThat(getInternalElement(0)).isEqualTo(34);
        assertThat(getInternalElement(1)).isEqualTo(78);
        assertThat(internalSize).isEqualTo(4);
    }

    @Test
    @Order(17)
    void setFirstElementWhenListIsEmpty() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.set(0, 34));
    }

    @Test
    @Order(18)
    void setByIndexEqualToSize() {
        addInternalElements(2, 3, 4); // size = 3

        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.set(3, 222));
    }

    @Test
    @Order(19)
    void get() {
        addInternalElements(25, 87, 45);

        int firstElement = intList.get(0);
        int secondElement = intList.get(1);
        int thirdElement = intList.get(2);

        assertThat(firstElement).isEqualTo(25);
        assertThat(secondElement).isEqualTo(87);
        assertThat(thirdElement).isEqualTo(45);

    }

    @Test
    @Order(20)
    void getByZeroIndexWhenListHasSingleElement() {
        addInternalElements(25);

        int element = intList.get(0);

        assertThat(element).isEqualTo(25);
    }

    @Test
    @Order(21)
    void getByZeroIndexWhenListIsEmpty() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.get(0));
    }

    @Test
    @Order(22)
    void getWhenIndexIsNegative() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.get(-1));
    }

    @Test
    @Order(23)
    void getWhenIndexIsEqualToListSize() {
        addInternalElements(33, 46, 25, 87, 45);

        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.get(5));
    }

    @Test
    @Order(24)
    void getFirst() {
        addInternalElements(31, 32, 5);

        int firstElement = intList.getFirst();

        assertThat(firstElement).isEqualTo(31);
    }

    @Test
    @Order(25)
    void getLast() {
        addInternalElements(41, 6, 42);

        int lastElement = intList.getLast();

        assertThat(lastElement).isEqualTo(42);
    }

    @Test
    @Order(26)
    void getFirstWhenListIsEmpty() {
        assertThatExceptionOfType(NoSuchElementException.class)
                .isThrownBy(() -> intList.getFirst());
    }

    @Test
    @Order(27)
    void getLastWhenListIsEmpty() {
        assertThatExceptionOfType(NoSuchElementException.class)
                .isThrownBy(() -> intList.getLast());
    }


    @Test
    @Order(28)
    void remove() {
        addInternalElements(1, 2, 3, 4, 5);

        int elementIndex = 2;
        int deletedElement = intList.remove(elementIndex); // element = 3

        int replacedElement = getInternalElement(elementIndex);

        assertThat(deletedElement).isEqualTo(3);
        assertThat(replacedElement).isEqualTo(4);
    }

    @Test
    @Order(29)
    void removeChangesSize() {
        addInternalElements(1, 2, 3, 4, 5);

        int elementIndex = 2;
        int deletedElement = intList.remove(elementIndex); // element = 3

        int size = getInternalSize();

        assertThat(deletedElement).isEqualTo(3);
        assertThat(size).isEqualTo(4);
    }

    @Test
    @Order(30)
    void removeFirst() {
        addInternalElements(4, 6, 8, 9);

        int deletedElement = intList.remove(0);

        int replacedElement = getInternalElement(0);
        int size = getInternalSize();

        assertThat(deletedElement).isEqualTo(4);
        assertThat(replacedElement).isEqualTo(6);
        assertThat(size).isEqualTo(3);
    }

    @Test
    @Order(31)
    void removeLast() {
        addInternalElements(4, 6, 8, 9);

        int deletedElement = intList.remove(getInternalSize() - 1);

        int newLastElement = getInternalElement(getInternalSize() - 1);
        int tailElement = (int) getNodeValue(TAIL_NODE_FIELD);

        int size = getInternalSize();

        assertThat(deletedElement).isEqualTo(9);
        assertThat(newLastElement).isEqualTo(8);
        assertThat(tailElement).isEqualTo(8);
        assertThat(size).isEqualTo(3);
    }

    @Test
    @Order(32)
    void removeWhenListIsEmpty() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.remove(234));
    }

    @Test
    @Order(33)
    void removeByZeroIndexWhenListIsEmpty() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> intList.remove(0));
    }

    @Test
    @Order(34)
    void size() {
        setInternalSize(5);

        int sizeFromMethod = intList.size();

        assertThat(sizeFromMethod).isEqualTo(5);
    }

    @Test
    @Order(35)
    void sizeWhenListIsEmpty() {
        int size = getInternalSize();

        assertThat(size).isEqualTo(0);
    }

    @Test
    @Order(36)
    void contains() {
        addInternalElements(45, 6, 3, 6);

        boolean containsExistingElement = intList.contains(3);
        boolean containsNotExistingElement = intList.contains(54);

        assertThat(containsExistingElement).isTrue();
        assertThat(containsNotExistingElement).isFalse();
    }

    @Test
    @Order(37)
    void containsWhenListIsEmpty() {
        boolean contains = intList.contains(34);

        assertThat(contains).isFalse();
    }

    @Test
    @Order(38)
    void isEmpty() {
        addInternalElements(34, 5, 6);

        boolean empty = intList.isEmpty();

        assertThat(empty).isFalse();
    }

    @Test
    @Order(39)
    void isEmptyWhenListIsEmpty() {
        boolean empty = intList.isEmpty();

        assertThat(empty).isTrue();
    }

    @Test
    @Order(40)
    void clearWhenListIsEmpty() {
        intList.clear();

        int size = getInternalSize();

        assertThat(size).isEqualTo(0);
    }

    @Test
    @Order(41)
    void clearChangesSize() {
        addInternalElements(4, 5, 6);

        intList.clear();

        int size = getInternalSize();

        assertThat(size).isEqualTo(0);
    }

    @Test
    @Order(42)
    void clearRemovesAllElements() {
        addInternalElements(4, 5, 6);

        intList.clear();

        assertThatExceptionOfType(NullPointerException.class)
                .isThrownBy(() -> getInternalElement(0));
    }

    @SneakyThrows
    private int getInternalElement(int index) {

        Object head = getAccessibleFieldByPredicate(intList, HEAD_NODE_FIELD).get(intList);

        for (int j = 0; j < index; j++) {
            head = getAccessibleFieldByPredicate(head, NODE_FIELD).get(head);
        }
        return (int) getAccessibleFieldByPredicate(head, ELEMENT_FIELD).get(head);
    }

    @SneakyThrows
    private int getInternalSize() {
        return (int) getAccessibleFieldByPredicate(intList, SIZE_FIELD).get(intList);
    }

    @SneakyThrows
    private void addInternalElements(int... elements) {
        Field nodeField = getInternalHeadField();
        Field tailNode = getInternalTailField();
        Class<?> nodeType = nodeField.getType();

        Object previousObject = intList;
        Object nodeObject = null;

        for (int element : elements) {
            nodeObject = createNodeObjectWithInternalElement(nodeType, element);
            nodeField.set(previousObject, nodeObject);
            nodeField = getAccessibleFieldByPredicate(nodeObject, NODE_FIELD);
            previousObject = nodeObject;
        }

        tailNode.set(intList, nodeObject);
        setInternalSize(elements.length);
    }

    private Field getInternalHeadField() {
        return getAccessibleFieldByPredicate(intList, HEAD_NODE_FIELD);
    }

    private Field getInternalTailField() {
        return getAccessibleFieldByPredicate(intList, TAIL_NODE_FIELD);
    }

    @SneakyThrows
    private void setInternalSize(int size) {
        Field sizeField = getAccessibleFieldByPredicate(intList, SIZE_FIELD);
        sizeField.setInt(intList, size);
    }

    @SneakyThrows
    private Object createNodeObjectWithInternalElement(Class<?> nodeClass, int element) {
        Object nodeObject;
        Constructor<?>[] declaredConstructors = nodeClass.getDeclaredConstructors();
        Constructor<?> constructor;

        constructor = declaredConstructors[0];
        constructor.setAccessible(true);
        if (constructor.getParameterTypes().length == 1) {
            nodeObject = constructor.newInstance(element);
        } else {
            nodeObject = createNodeByConstructorWithoutParameters(element, constructor);
        }
        return nodeObject;
    }

    @SneakyThrows
    private Object createNodeByConstructorWithoutParameters(int element, Constructor<?> constructor) {
        Object nodeObject;
        nodeObject = constructor.newInstance();
        Field nodeElement = getAccessibleFieldByPredicate(nodeObject, ELEMENT_FIELD);
        nodeElement.set(nodeObject, element);
        return nodeObject;
    }

    private Field getAccessibleFieldByPredicate(Object object, Predicate<Field> predicate) {
        Field field = Arrays.stream(object.getClass().getDeclaredFields())
                .filter(predicate)
                .findAny()
                .orElseThrow();
        field.setAccessible(true);
        return field;
    }

    @SneakyThrows
    private Object getNodeValue(Predicate<Field> predicate) {
        Object field = getAccessibleFieldByPredicate(intList, predicate).get(intList);
        final Field value = getAccessibleFieldByPredicate(field, ELEMENT_FIELD);
        value.setAccessible(true);
        return value.get(field);
    }
}

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-5-array-list/README.MD
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Array List

Build strong skills implementing a well known Array List data structure 💪

### Pre-conditions ❗

* You're supposed to be familiar `List` collection and generics in Java

### Objectives

* use array of type `Object` to store any elements ✅
* resize array using native method `System.arrayCopy()` ✅
* **add an element** to the end of array ✅
* **add an element by index** (shift whole array tail to the right) ✅
* **set element** by index ✅
* **remove element** by index (shift whole array tail to the left) ✅
* maintain list **size** ✅

### Exercise overview 🇺🇦
[![Watch the video](https://img.youtube.com/vi/jFBKToSC3ag/0.jpg)](https://www.youtube.com/watch?v=jFBKToSC3ag)

---

#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-5-array-list/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>2-0-data-structures-and-algorithms</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>2-2-5-array-list</artifactId>


    <dependencies>
        <dependency>
            <groupId>com.bobocode</groupId>
            <artifactId>data-structures-and-algorithms-util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-5-array-list/src/main/java/com/bobocode/cs/ArrayList.java
================================================
package com.bobocode.cs;

import com.bobocode.util.ExerciseNotCompletedException;

/**
 * {@link ArrayList} is an implementation of {@link List} interface. This resizable data structure
 * based on an array and is simplified version of {@link java.util.ArrayList}.
 * <p><p>
 * <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com">visit our website</a></strong>
 * <p>
 *
 * @author Serhii Hryhus
 */
public class ArrayList<T> implements List<T> {

    /**
     * This constructor creates an instance of {@link ArrayList} with a specific capacity of an array inside.
     *
     * @param initCapacity - the initial capacity of the list
     * @throws IllegalArgumentException – if the specified initial capacity is negative or 0.
     */
    public ArrayList(int initCapacity) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * This constructor creates an instance of {@link ArrayList} with a default capacity of an array inside.
     * A default size of inner array is 5;
     */
    public ArrayList() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Creates and returns an instance of {@link ArrayList} with provided elements
     *
     * @param elements to add
     * @return new instance
     */
    public static <T> List<T> of(T... elements) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Adds an element to the array.
     *
     * @param element element to add
     */
    @Override
    public void add(T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Adds an element to the specific position in the array where
     *
     * @param index   index of position
     * @param element element to add
     */
    @Override
    public void add(int index, T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Retrieves an element by its position index. In case provided index in out of the list bounds it
     * throws {@link IndexOutOfBoundsException}
     *
     * @param index index of element
     * @return en element
     */
    @Override
    public T get(int index) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Returns the first element of the list. Operation is performed in constant time O(1)
     *
     * @return the first element of the list
     * @throws java.util.NoSuchElementException if list is empty
     */
    @Override
    public T getFirst() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Returns the last element of the list. Operation is performed in constant time O(1)
     *
     * @return the last element of the list
     * @throws java.util.NoSuchElementException if list is empty
     */
    @Override
    public T getLast() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Changes the value of array at specific position. In case provided index in out of the list bounds it
     * throws {@link IndexOutOfBoundsException}
     *
     * @param index   position of value
     * @param element a new value
     */
    @Override
    public void set(int index, T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Removes an elements by its position index. In case provided index in out of the list bounds it
     * throws {@link IndexOutOfBoundsException}
     *
     * @param index element index
     * @return deleted element
     */
    @Override
    public T remove(int index) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Checks for existing of a specific element in the list.
     *
     * @param element is element
     * @return If element exists method returns true, otherwise it returns false
     */
    @Override
    public boolean contains(T element) {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Checks if a list is empty
     *
     * @return {@code true} if list is empty, {@code false} otherwise
     */
    @Override
    public boolean isEmpty() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * @return amount of saved elements
     */
    @Override
    public int size() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }

    /**
     * Removes all list elements
     */
    @Override
    public void clear() {
        throw new ExerciseNotCompletedException(); // todo: implement this method
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-5-array-list/src/test/java/com/bobocode/cs/ArrayListTest.java
================================================
package com.bobocode.cs;

import lombok.SneakyThrows;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import java.lang.reflect.Field;
import java.util.NoSuchElementException;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;

/**
 * A reflection-based test class for {@link ArrayList}.
 * <p>
 * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.
 *
 * @author Serhii Hryhus
 * @author Ivan Virchenko
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ArrayListTest {

    private List<Integer> arrayList = new ArrayList<>();

    @Test
    @Order(1)
    void add() {
        arrayList.add(10);
        arrayList.add(15);
        arrayList.add(20);

        Object[] internalArray = getTestArray();

        assertThat(internalArray[0]).isEqualTo(10);
        assertThat(internalArray[1]).isEqualTo(15);
        assertThat(internalArray[2]).isEqualTo(20);
    }

    @Test
    @Order(2)
    void sizeOfEmptyArrayWrapper() {
        assertThat(arrayList.size()).isEqualTo(0);
    }

    @Test
    @Order(3)
    void size() {
        setTestSize(3);
        assertThat(arrayList.size()).isEqualTo(3);
    }

    @Test
    @Order(4)
    void getElementsByIndex() {
        fillTestArray(10, 15, 20);

        assertThat(arrayList.get(0)).isEqualTo(10);
        assertThat(arrayList.get(1)).isEqualTo(15);
        assertThat(arrayList.get(2)).isEqualTo(20);
        assertThat(arrayList.size()).isEqualTo(3);
    }

    @Test
    @Order(5)
    void getFirstElement() {
        fillTestArray(31, 24);
        assertThat(arrayList.getFirst()).isEqualTo(31);
    }

    @Test
    @Order(6)
    void getLastElement() {
        fillTestArray(31, 34);
        assertThat(arrayList.getLast()).isEqualTo(34);
    }

    @Test
    @Order(7)
    void getFirstOfEmptyList() {
        assertThatExceptionOfType(NoSuchElementException.class)
                .isThrownBy(() -> arrayList.getFirst());
    }

    @Test
    @Order(8)
    void getLastOfEmptyList() {
        assertThatExceptionOfType(NoSuchElementException.class)
                .isThrownBy(() -> arrayList.getLast());
    }

    @Test
    @Order(9)
    void createListWithSpecificArrayCapacity() {
        arrayList = new ArrayList<>(8);
        assertThat(getTestArray().length).isEqualTo(8);
    }

    @Test
    @Order(10)
    void createListWithWrongCapacity() {
        assertThatExceptionOfType(IllegalArgumentException.class)
                .isThrownBy(() -> arrayList = new ArrayList<>(-2));
    }

    @Test
    @Order(11)
    void addElements() {
        arrayList = ArrayList.of(15, 69, 58, 78);

        assertThat(getTestArray()[0]).isEqualTo(15);
        assertThat(getTestArray()[1]).isEqualTo(69);
        assertThat(getTestArray()[2]).isEqualTo(58);
        assertThat(getTestArray()[3]).isEqualTo(78);
        assertThat(getTestSize()).isEqualTo(4);
    }

    @Test
    @Order(12)
    void addShouldResizeDefaultCapacityWhenArrayIsFull() {
        arrayList = new ArrayList<>();
        int defaultCapacity = getTestArray().length;

        arrayList.add(15);
        arrayList.add(69);
        arrayList.add(58);
        arrayList.add(78);
        arrayList.add(6);
        arrayList.add(33);
        arrayList.add(21);

        assertThat(getTestArray().length).isGreaterThan(defaultCapacity);
        assertThat(getTestSize()).isEqualTo(7);
    }

    @Test
    @Order(13)
    void addShouldResizeSpecificCapacityWhenArrayIsFull() {
        arrayList = new ArrayList<>(4);

        arrayList.add(15);
        arrayList.add(69);
        arrayList.add(58);
        arrayList.add(78);
        arrayList.add(6);
        arrayList.add(33);
        arrayList.add(21);

        assertThat(getTestArray().length).isGreaterThan(4);
        assertThat(getTestSize()).isEqualTo(7);
    }

    @Test
    @Order(14)
    void addElementByIndex() {
        fillTestArray(15, 69, 58, 78, 68);

        arrayList.add(50);
        arrayList.add(2, 10);

        Object[] internalArray = getTestArray();

        assertThat(internalArray[2]).isEqualTo(10);
        assertThat(internalArray[5]).isEqualTo(68);
        assertThat(getTestSize()).isEqualTo(7);
    }

    @Test
    @Order(15)
    void addElementByNegativeIndex() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.add(-1, 66));
    }

    @Test
    @Order(16)
    void addElementByIndexLargerThanListSize() {
        setTestSize(4);
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.add(5, 88));
    }

    @Test
    @Order(17)
    void addElementByIndexEqualToSize() {
        fillTestArray(1, 2, 3, 4, 5);

        arrayList.add(5, 111);

        Object[] internalArray = getTestArray();

        assertThat(internalArray[5]).isEqualTo(111);
        assertThat(getTestSize()).isEqualTo(6);
    }

    @Test
    @Order(18)
    void getFirstElementFromEmptyList() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.get(0));
    }

    @Test
    @Order(19)
    void getElementByNegativeIndex() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.get(-1));
    }

    @Test
    @Order(20)
    void getElementByIndexThrowsExceptionWhenIndexIsOutOfBound() {
        fillTestArray(1, 2, 3, 4);
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.get(4));
    }

    @Test
    @Order(21)
    void setElementByIndex() {
        fillTestArray(15, 69, 58, 78);
        Object[] internalArray = getTestArray();

        arrayList.set(2, 10);

        assertThat(internalArray[2]).isEqualTo(10);
        assertThat(internalArray[3]).isEqualTo(78);
        assertThat(getTestSize()).isEqualTo(4);
    }

    @Test
    @Order(22)
    void setElementByIndexThrowsExceptionWhenIndexIsOutOfBound() {
        fillTestArray(15, 69, 58, 78);
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.set(4, 10));
    }

    @Test
    @Order(23)
    void setFirstElementOnEmptyList() {
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.set(0, 34));
    }

    @Test
    @Order(24)
    void removeElementByIndex() {
        fillTestArray(15, 69, 58, 78, 100);
        Object[] internalArray = getTestArray();

        int removedElement = arrayList.remove(2);

        assertThat(internalArray[2]).isEqualTo(78);
        assertThat(internalArray[1]).isEqualTo(69);
        assertThat(getTestSize()).isEqualTo(4);
        assertThat(removedElement).isEqualTo(58);
    }

    @Test
    @Order(25)
    void removeElementByIndexThrowsExceptionWhenIndexEqualsSize() {
        fillTestArray(15, 69, 58, 78);
        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.remove(4));
    }

    @Test
    @Order(26)
    void removeLastElementByIndex() {
        fillTestArray(15, 69, 58, 78, 100);
        Object[] internalArray = getTestArray();

        int removedElement = arrayList.remove(4);

        assertThat(internalArray[3]).isEqualTo(78);
        assertThat(getTestSize()).isEqualTo(4);
        assertThat(removedElement).isEqualTo(100);
    }

    @Test
    @Order(27)
    void removeElementByIndexThrowsExceptionWhenIndexIsOutOfBounds() {
        fillTestArray(15, 69, 58, 78, 100);

        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.remove(6));
    }

    @Test
    @Order(28)
    void containsOnEmptyList() {
        assertThat(arrayList.contains(8)).isEqualTo(false);
    }

    @Test
    @Order(29)
    void containsElement() {
        fillTestArray(15, 69, 58, 78, 100);
        assertThat(arrayList.contains(58)).isEqualTo(true);
    }

    @Test
    @Order(30)
    void containsNotExistingWhenArrayIsNotFilled() {
        arrayList = new ArrayList<>(100);
        Object[] internalArray = getTestArray();
        internalArray[0] = 5;
        internalArray[1] = 10;

        boolean result = arrayList.contains(3);

        assertThat(result).isFalse();
    }

    @Test
    @Order(31)
    void findNotExistingElement() {
        fillTestArray(15, 69, 58, 78, 100);
        assertThat(arrayList.contains(200)).isEqualTo(false);
    }

    @Test
    @Order(32)
    void isEmptyOnEmptyList() {
        assertThat(arrayList.isEmpty()).isEqualTo(true);
    }

    @Test
    @Order(33)
    void isEmpty() {
        setTestSize(3);
        assertThat(arrayList.isEmpty()).isEqualTo(false);
    }

    @Test
    @Order(34)
    void clearOnEmptyList() {
        assertThat(arrayList.isEmpty()).isEqualTo(true);
    }

    @Test
    @Order(35)
    void clearChangesTheSize() {
        setTestSize(100);
        arrayList.clear();

        assertThat(arrayList.size()).isEqualTo(0);
    }

    @Test
    @Order(36)
    void clearRemovesElements() {
        fillTestArray(4, 5, 6);

        arrayList.clear();

        assertThatExceptionOfType(IndexOutOfBoundsException.class)
                .isThrownBy(() -> arrayList.get(0));
    }

    @SneakyThrows
    private void setTestSize(int size) {
        Field sizeField = arrayList.getClass().getDeclaredField("size");
        sizeField.setAccessible(true);
        sizeField.set(arrayList, size);
    }

    @SneakyThrows
    private int getTestSize() {
        Field testSize = arrayList.getClass().getDeclaredField("size");
        testSize.setAccessible(true);
        return (int) testSize.get(arrayList);
    }

    @SneakyThrows
    private Object[] getTestArray() {
        Field field = arrayList.getClass().getDeclaredField(getTestArrayName());
        field.setAccessible(true);
        return (Object[]) field.get(arrayList);
    }

    private String getTestArrayName() {
        Field[] fields = arrayList.getClass().getDeclaredFields();
        String name = null;
        for (Field field : fields) {
            if (field.getType().isArray()) {
                field.setAccessible(true);
                name = field.getName();
            }
        }
        return name;
    }

    @SneakyThrows
    private void fillTestArray(Object... elements) {
        Field arrayField = arrayList.getClass().getDeclaredField(getTestArrayName());
        Field sizeField = arrayList.getClass().getDeclaredField("size");
        arrayField.setAccessible(true);
        sizeField.setAccessible(true);
        arrayField.set(arrayList, elements);
        sizeField.set(arrayList, elements.length);
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/README.md
================================================
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Binary Search Tree
Learn tree data structures and build strong skills implementing binary search tree using recursion 💪

### Pre-conditions ❗️
* You're supposed to be familiar with trees like [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree)
* You should understand the [recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science))

### Objectives
* implement a generic class `Node<T>` with left and right child nodes ✅
* **insert  an element** to the tree using recursion ✅
* **search an element** in the tree ✅
* **traverse tree elements** in a ascending order ✅
* calculate tree **depth** ✅
* maintain tree **size** ✅


### Exercise overview 🇺🇦
[![Watch the video](https://img.youtube.com/vi/alxzyWswCVg/0.jpg)](https://www.youtube.com/watch?v=alxzyWswCVg)

---
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-exercises/tree/main/0-0-intro#introduction)

##
<div align="center"><img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/animation/GitHub%20Star_3.gif" height=50/></div>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>2-0-data-structures-and-algorithms</artifactId>
        <groupId>com.bobocode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>2-2-6-binary-search-tree</artifactId>


</project>

================================================
FILE: 2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/main/java/com/bobocode/cs/BinarySearchTree.java
================================================
package com.bobocode.cs;

import java.util.function.Consumer;

public interface BinarySearchTree<T extends Comparable<T>> {
    /**
     * insert an element
     * @return true if element did not exist in the tree and was inserted successfully
     */
    boolean insert(T element);

    /**
     * @return true if tree contains element
     */
    boolean contains(T element);

    /**
     * @return number of elements in the tree
     */
    int size();

    /**
     * @return max. number of transition between root node and any other node; 0 - if tree is empty or contains 1 element
     */
    int depth();

    /**
     * traverse the tree in element's natural order
     * @param consumer accepts ref. to node during traversing
     */
    void inOrderTraversal(Consumer<T> consumer);
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/main/java/com/bobocode/cs/RecursiveBinarySearchTree.java
================================================
package com.bobocode.cs;

import com.bobocode.util.ExerciseNotCompletedException;

import java.util.function.Consumer;

/**
 * {@link RecursiveBinarySearchTree} is an implementation of a {@link BinarySearchTree} that is based on a linked nodes
 * and recursion. A tree node is represented as a nested class {@link Node}. It holds an element (a value) and
 * two references to the left and right child nodes.
 * <p><p>
 * <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com">visit our website</a></strong>
 * <p>
 *
 * @param <T> a type of elements that are stored in the tree
 * @author Taras Boychuk
 * @author Maksym Stasiuk
 */
public class RecursiveBinarySearchTree<T extends Comparable<T>> implements BinarySearchTree<T> {

    public static <T extends Comparable<T>> RecursiveBinarySearchTree<T> of(T... elements) {
        throw new ExerciseNotCompletedException();
    }

    @Override
    public boolean insert(T element) {
        throw new ExerciseNotCompletedException();
    }

    @Override
    public boolean contains(T element) {
        throw new ExerciseNotCompletedException();
    }

    @Override
    public int size() {
        throw new ExerciseNotCompletedException();
    }

    @Override
    public int depth() {
        throw new ExerciseNotCompletedException();
    }

    @Override
    public void inOrderTraversal(Consumer<T> consumer) {
        throw new ExerciseNotCompletedException();
    }
}


================================================
FILE: 2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/test/java/com/bobocode/cs/RecursiveBinarySearchTreeTest.java
================================================
package com.bobocode.cs;

import lombok.SneakyThrows;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.junit.jupiter.params.provider.Arguments.arguments;

/**
 * A reflection-based test class for {@link ArrayList}.
 * <p>
 * PLEASE NOTE: we use Reflection API only for learning purposes. It should NOT be used for production tests.
 *
 * @author Ivan Virchenko
 * @author Maksym Stasiuk
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class RecursiveBinarySearchTreeTest {
    private static final Predicate<Field> SIZE_FIELD = field ->
            field.getName().toLowerCase().contains("size") || field.getName().toLowerCase().contains("length");
    
    private static final Predicate<Field> NODE_FIELD = field ->
            field.getType().getSimpleName().equals("Node");
    
    private static final Predicate<Field> ELEMENT_FIELD = field ->
            field.getName().toLowerCase().contains("element")
            || field.getName().toLowerCase().contains("item")
            || field.getName().toLowerCase().contains("value");
    
    private static final Predicate<Field> LEFT_FIELD = field ->
            field.getName().toLowerCase().contains("left")
            && field.getType().getSimpleName().equals("Node");
    
    private static final Predicate<Field> RIGHT_FIELD = field ->
            field.getName().toLowerCase().contains("right")
            && field.getType().getSimpleName().equals("Node");

    private static final Integer[] someElements = {10, 9, 11, 8, 12, 7};
    
    private BinarySearchTree<Integer> tree = new RecursiveBinarySearchTree<>();

    @Test
    @Order(1)
    void properNodeClassNameCheck() {
        Class<?> innerClass = getInnerClass();
        String name = innerClass.getSimpleName();

        assertThat(name).isEqualTo("Node");
    }

    @Test
    @Order(2)
    void properTreeFieldsCheck() {
        Class<?> treeClass = tree.getClass();

        boolean hasSizeField = Arrays.stream(treeClass.getDeclaredFields())
                .anyMatch(SIZE_FIELD);

        boolean hasNodeField = Arrays.stream(treeClass.getDeclaredFields())
                .anyMatch(NODE_FIELD);

        assertThat(hasSizeField).isTrue();
        assertThat(hasNodeField).isTrue();
    }

    @Test
    @Order(3)
    void properNodeFieldsCheck() {
        Class<?> innerClass = getInnerClass();

        boolean isElement = Arrays.stream(innerClass.getDeclaredFields())
                .anyMatch(ELEMENT_FIELD);

        boolean isLeft = Arrays.stream(innerClass.getDeclaredFields())
                .anyMatch(LEFT_FIELD);

        boolean isRight = Arrays.stream(innerClass.getDeclaredFields())
                .anyMatch(RIGHT_FIELD);

        assertThat(isElement).isTrue();
        assertThat(isLeft).isTrue();
        assertThat(isRight).isTrue();
    }

    @Test
    @Order(4)
    void of() {
        tree = RecursiveBinarySearchTree.of(someElements);
        for (var e : someElements) {
            assertThat(contains(getRootObject(), e)).isTrue();
        }
        assertThat(getInnerSize()).isEqualTo(someElements.length);
    }

    @Test
    @Order(5)
    void insert() {
        for (Integer e : someElements) {
            assertThat(contains(getRootObject(), e)).isFalse(); //does not contain
            assertThat(tree.insert(e)).isTrue(); //do insert
            assertThat(contains(getRootObject(), e)).isTrue(); //and contains
        }
    }

    @Test
    @Order(6)
    void insertToRootIfTreeIsEmpty() {
        assertThat(getRootObject()).isNull();
        tree.insert(10);
        assertThat(getRootObject()).isNotNull();
    }

    @Test
    @Order(7)
    void insertLeftIfLessThanRoot() {
        tree.insert(10); //root
        tree.insert(5); //left

        assertThat(getLeftNode(getRootObject())).isNotNull();
    }

    @Test
    @Order(8)
    void insertRightIfGreaterThanRoot() {
        tree.insert(10); //root
        tree.insert(15); //right

        assertThat(getRightNode(getRootObject())).isNotNull();
    }

    @Test
    @Order(9)
    void insertDoesNotAddDuplicateElements() {
        fillTestTree(10, 11, 12);

        assertThat(tree.insert(10)).isFalse();
        assertThat(tree.insert(11)).isFalse();
        assertThat(tree.insert(12)).isFalse();
    }

    @Test
    @Order(10)
    void insertThrowsExceptionWhenArgumentIsNull() {
        fillTestTree(someElements);
        assertThatNullPointerException().isThrownBy(
                () -> tree.insert(null)
        );
    }

    @Test
    @Order(11)
    void containsReturnsTrueIfElementExist() {
        fillTestTree(9, 10, 11);

        assertThat(tree.contains(10)).isTrue();
        assertThat(tree.contains(9)).isTrue();
        assertThat(tree.contains(11)).isTrue();
    }

    @Test
    @Order(12)
    void containsReturnsFalseIfElementDoesntExist() {
        fillTestTree(someElements);
        assertThat(tree.contains(100)).isFalse();
    }

    @Test
    @Order(13)
    void containsThrowsExceptionIFParameterIsNull() {
        assertThatNullPointerException().isThrownBy(() -> tree.contains(null));
    }

    @Test
    @Order(14)
    void sizeIsGrowingWhenInserting() {
        tree.insert(10);
        tree.insert(15);
        tree.insert(20);

        assertThat(getInnerSize()).isEqualTo(3);
    }

    @Test
    @Order(15)
    void sizeDoesNotGrowWhenInsertingNotUnique() {
        fillTestTree(10, 11, 12);

        assertThat(getInnerSize()).isEqualTo(3);

        tree.insert(10);
        tree.insert(11);
        tree.insert(12);

        assertThat(getInnerSize()).isEqualTo(3);
    }

    @Order(16)
    @ParameterizedTest
    @MethodSource("depthArguments")
    void depth(Integer[] elements, int depth) {
        fillTestTree(elements);
        assertThat(tree.depth()).isEqualTo(depth);
    }

    @Test
    @Order(17)
    void depthGrowWhenInsert() {

        tree.insert(13);
        tree.insert(11);
        tree.insert(12);
        tree.insert(15);
        tree.insert(-15);

        assertThat(tree.depth()).isGreaterThan(0);
    }

    @Test
    @Order(18)
    void depthIsZeroIfRootIsNull() {
        assertThat(tree.depth()).isEqualTo(0);
    }

    @Test
    @Order(19)
    void inorderTraversal() {
        fillTestTree(someElements);
        Integer[] sortedElements = Arrays.copyOf(someElements, someElements.length);
        Arrays.sort(sortedElements);

        List<Integer> traversedElements = new ArrayList<>(getInnerSize());
        tree.inOrderTraversal(traversedElements::add);

        assertThat(traversedElements).isEqualTo(List.of(sortedElements));
    }

    public static Stream<Arguments> depthArguments() {
        return Stream.of(
                //empty tree
                arguments(new Integer[]{}, 0),
                //tree with a single element
                arguments(new Integer[]{24}, 0),
                /*
                 * .......10
                 * ....../  \
                 * .....5   15
                 * ..../      \
                 * ...1       20
                 */
                arguments(new Integer[]{10, 5, 15, 1, 20}, 2),
                /*
                 * ..1
                 * ...\
                 * ....2
                 * .....\
                 * ..... 3
                 * .......\
                 * ........4
                 * .........\
                 * ..........5
       
Download .txt
gitextract_r1nqldzq/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── maven.yml
│       └── projectActions.yml
├── .gitignore
├── .mvn/
│   └── wrapper/
│       ├── MavenWrapperDownloader.java
│       ├── maven-wrapper.jar
│       └── maven-wrapper.properties
├── 0-0-intro/
│   ├── README.md
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── com/
│       │           └── bobocode/
│       │               └── intro/
│       │                   └── ExerciseIntroduction.java
│       └── test/
│           └── java/
│               └── com/
│                   └── bobocode/
│                       └── intro/
│                           └── ExerciseIntroductionTest.java
├── 1-0-java-basics/
│   ├── 1-3-0-hello-generics/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── basics/
│   │       │                   ├── Box.java
│   │       │                   └── BoxDemoApp.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── basics/
│   │                           └── BoxTest.java
│   ├── 1-3-1-crazy-generics/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── basics/
│   │       │                   ├── CrazyGenerics.java
│   │       │                   └── util/
│   │       │                       └── BaseEntity.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── basics/
│   │                           └── CrazyGenericsTest.java
│   ├── 1-3-2-heterogeneous-max-holder/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── basics/
│   │       │                   └── HeterogeneousMaxHolder.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── basics/
│   │                           └── HeterogeneousMaxHolderTest.java
│   ├── 1-5-0-hello-annotations/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── basics/
│   │       │                   ├── HelloAnnotationsExercise.java
│   │       │                   └── Level.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── basics/
│   │                           └── HelloAnnotationsExerciseTest.java
│   ├── README.md
│   └── pom.xml
├── 2-0-data-structures-and-algorithms/
│   ├── 2-2-1-node/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobobode/
│   │       │               └── cs/
│   │       │                   ├── Node.java
│   │       │                   └── Nodes.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── NodesTest.java
│   ├── 2-2-2-stack/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   ├── LinkedStack.java
│   │       │                   ├── Stack.java
│   │       │                   └── exception/
│   │       │                       └── EmptyStackException.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── LinkedStackTest.java
│   ├── 2-2-3-linked-queue/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   ├── LinkedQueue.java
│   │       │                   └── Queue.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── LinkedQueueTest.java
│   ├── 2-2-4-linked-list/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   └── LinkedList.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── LinkedListTest.java
│   ├── 2-2-5-array-list/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   └── ArrayList.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── ArrayListTest.java
│   ├── 2-2-6-binary-search-tree/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   ├── BinarySearchTree.java
│   │       │                   └── RecursiveBinarySearchTree.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── RecursiveBinarySearchTreeTest.java
│   ├── 2-2-9-hash-table/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── cs/
│   │       │                   ├── HashTable.java
│   │       │                   └── Map.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── HashTableTest.java
│   ├── README.md
│   ├── data-structures-and-algorithms-util/
│   │   ├── pom.xml
│   │   └── src/
│   │       └── main/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── cs/
│   │                           └── List.java
│   └── pom.xml
├── 3-0-java-core/
│   ├── 3-6-1-file-reader/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── se/
│   │       │                   └── FileReaders.java
│   │       └── test/
│   │           ├── java/
│   │           │   └── com/
│   │           │       └── bobocode/
│   │           │           └── se/
│   │           │               └── FileReadersTest.java
│   │           └── resources/
│   │               ├── empty.txt
│   │               ├── lines.txt
│   │               └── simple.txt
│   ├── 3-6-2-file-stats/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── se/
│   │       │                   ├── FileStats.java
│   │       │                   └── FileStatsException.java
│   │       └── test/
│   │           ├── java/
│   │           │   └── com/
│   │           │       └── bobocode/
│   │           │           └── se/
│   │           │               └── FileStatsTest.java
│   │           └── resources/
│   │               ├── scosb.txt
│   │               └── sotl.txt
│   ├── 3-6-3-crazy-regex/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── se/
│   │       │                   └── CrazyRegex.java
│   │       └── test/
│   │           ├── java/
│   │           │   └── com/
│   │           │       └── bobocode/
│   │           │           └── se/
│   │           │               └── CrazyRegexTest.java
│   │           └── resources/
│   │               ├── nasa.json
│   │               └── note.txt
│   ├── 3-6-4-random-field-comparator/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── se/
│   │       │                   └── RandomFieldComparator.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── se/
│   │                           └── RandomFieldComparatorTest.java
│   ├── README.md
│   └── pom.xml
├── 4-0-object-oriented-programming/
│   ├── 4-3-1-flight-search/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── oop/
│   │       │                   ├── data/
│   │       │                   │   └── FlightDao.java
│   │       │                   ├── factory/
│   │       │                   │   └── FlightServiceFactory.java
│   │       │                   └── service/
│   │       │                       └── FlightService.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── oop/
│   │                           └── FlightServiceTest.java
│   ├── README.md
│   └── pom.xml
├── 5-0-functional-programming/
│   ├── 5-0-1-lambda-functions-map/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── FunctionMap.java
│   │       │                   ├── Functions.java
│   │       │                   └── exception/
│   │       │                       └── InvalidFunctionNameException.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── FunctionsTest.java
│   ├── 5-0-2-stream-sum-of-squares/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── SumOfSquares.java
│   │       │                   └── exception/
│   │       │                       └── InvalidRangeException.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── SumOfSquaresTest.java
│   ├── 5-1-1-crazy-lambdas/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   └── CrazyLambdas.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── CrazyLambdasTest.java
│   ├── 5-2-1-crazy-streams/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── CrazyStreams.java
│   │       │                   └── exception/
│   │       │                       └── EntityNotFoundException.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── CrazyStreamsTest.java
│   ├── 5-3-1-crazy-optionals/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── CrazyOptionals.java
│   │       │                   ├── exception/
│   │       │                   │   └── AccountNotFoundException.java
│   │       │                   └── function/
│   │       │                       ├── AccountProvider.java
│   │       │                       ├── AccountService.java
│   │       │                       └── CreditAccountProvider.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── CrazyOptionalsTest.java
│   ├── 5-4-1-fun-prime-numbers/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── fp/
│   │       │                   ├── OOSumOfPrimes.java
│   │       │                   └── PrimeNumbers.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── fp/
│   │                           └── PrimeNumbersTest.java
│   ├── README.md
│   ├── pom.xml
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── bobocode/
│                       ├── lambdas/
│                       │   └── tutorial/
│                       │       ├── CrazyLambdaExample.java
│                       │       ├── FunctionComposition.java
│                       │       ├── LambdaAndMethodReference.java
│                       │       ├── LambdaComparatorExample.java
│                       │       ├── LambdaRunnableExample.java
│                       │       ├── MethodRefToUpperCaseExample.java
│                       │       ├── MethodReferenceExamples.java
│                       │       ├── PredefinedInterfacePrimitives.java
│                       │       └── PredefinedInterfacesExamples.java
│                       ├── optionals/
│                       │   └── tutorial/
│                       │       ├── OptionalCreation.java
│                       │       ├── OptionalExamples.java
│                       │       ├── OptionalImperativeVsDeclarativeCheck.java
│                       │       ├── OptionalMapping.java
│                       │       ├── OptionalPrimitives.java
│                       │       ├── OptionalReturningMethod.java
│                       │       ├── OptionalSearchByEmailExample.java
│                       │       ├── OptionalStream.java
│                       │       └── README.MD
│                       └── streams/
│                           └── tutorial/
│                               ├── ImperativeVsDeclarativeFiltering.java
│                               ├── ImperativeVsDeclarativeMax.java
│                               ├── README.MD
│                               ├── StreamAdditionalFeatures.java
│                               ├── StreamBasics.java
│                               ├── StreamCollecting.java
│                               ├── StreamFiltering.java
│                               ├── StreamMapping.java
│                               ├── StreamParallelProcessing.java
│                               ├── StreamPrimitives.java
│                               ├── StreamReducing.java
│                               ├── StreamSideEffectFilteringExample.java
│                               └── StreamWhileExample.java
├── 6-0-test-driven-development/
│   ├── 6-1-1-stack/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── tdd/
│   │       │                   ├── LinkedStack.java
│   │       │                   └── Stack.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── tdd/
│   │                           └── StackTest.java
│   ├── 6-1-2-linked-list/
│   │   ├── README.MD
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── tdd/
│   │       │                   ├── LinkedList.java
│   │       │                   └── List.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── tdd/
│   │                           └── LinkedListTest.java
│   ├── 6-1-3-binary-search-tree/
│   │   ├── README.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── bobocode/
│   │       │               └── tdd/
│   │       │                   ├── BinarySearchTree.java
│   │       │                   └── RecursiveBinarySearchTree.java
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── bobocode/
│   │                       └── tdd/
│   │                           └── BinarySearchTreeTest.java
│   ├── README.md
│   └── pom.xml
├── CONTRIBUTING.MD
├── LICENSE
├── README.md
├── java-fundamentals-util/
│   ├── pom.xml
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── bobocode/
│                       ├── data/
│                       │   └── Accounts.java
│                       ├── model/
│                       │   ├── Account.java
│                       │   ├── CreditAccount.java
│                       │   └── Sex.java
│                       └── util/
│                           └── ExerciseNotCompletedException.java
├── lesson-demo/
│   ├── pom.xml
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── bobocode/
│                       └── DemoApp.java
├── mvnw
├── mvnw.cmd
└── pom.xml
Download .txt
SYMBOL INDEX (1067 symbols across 113 files)

FILE: .mvn/wrapper/MavenWrapperDownloader.java
  class MavenWrapperDownloader (line 21) | public class MavenWrapperDownloader {
    method main (line 48) | public static void main(String args[]) {
    method downloadFileFromURL (line 97) | private static void downloadFileFromURL(String urlString, File destina...

FILE: 0-0-intro/src/main/java/com/bobocode/intro/ExerciseIntroduction.java
  class ExerciseIntroduction (line 18) | public class ExerciseIntroduction {
    method getWelcomeMessage (line 25) | public String getWelcomeMessage() {
    method encodeMessage (line 41) | public String encodeMessage(String message) {

FILE: 0-0-intro/src/test/java/com/bobocode/intro/ExerciseIntroductionTest.java
  class ExerciseIntroductionTest (line 23) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method getWelcomeMessage (line 28) | @Test
    method encodeMessageReturnsCorrectPhrase (line 37) | @Test

FILE: 1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/Box.java
  class Box (line 10) | public class Box {
    method Box (line 13) | public Box(Object value) {
    method getValue (line 17) | public Object getValue() {
    method setValue (line 21) | public void setValue(Object value) {

FILE: 1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/BoxDemoApp.java
  class BoxDemoApp (line 10) | public class BoxDemoApp {
    method main (line 11) | public static void main(String[] args) {

FILE: 1-0-java-basics/1-3-0-hello-generics/src/test/java/com/bobocode/basics/BoxTest.java
  class BoxTest (line 8) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method boxClassHasOneTypeParameter (line 12) | @Test
    method typeParameterIsCalledT (line 21) | @Test
    method typeParameterIsNotBounded (line 30) | @Test
    method valueFieldIsGeneric (line 40) | @Test
    method constructorParameterIsGeneric (line 51) | @Test
    method getterReturnTypeIsGeneric (line 63) | @Test
    method setterParameterIsGeneric (line 73) | @Test

FILE: 1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java
  class CrazyGenerics (line 28) | public class CrazyGenerics {
    class Sourced (line 35) | @Data
    class Limited (line 47) | @Data
    type Converter (line 62) | public interface Converter { // todo: introduce type parameters
    class MaxHolder (line 73) | public static class MaxHolder { // todo: refactor class to make it gen...
      method MaxHolder (line 76) | public MaxHolder(Object max) {
      method put (line 85) | public void put(Object val) {
      method getMax (line 89) | public Object getMax() {
    type StrictProcessor (line 100) | interface StrictProcessor { // todo: make it generic
      method process (line 101) | void process(Object obj);
    type CollectionRepository (line 111) | interface CollectionRepository { // todo: update interface according t...
      method save (line 112) | void save(Object entity);
      method getEntityCollection (line 114) | Collection<Object> getEntityCollection();
    type ListRepository (line 123) | interface ListRepository { // todo: update interface according to the ...
    type ComparableCollection (line 136) | interface ComparableCollection { // todo: refactor it to make generic ...
    class CollectionUtil (line 142) | static class CollectionUtil {
      method print (line 150) | public static void print(List<Integer> list) {
      method hasNewEntities (line 163) | public static boolean hasNewEntities(Collection<BaseEntity> entities) {
      method isValidCollection (line 176) | public static boolean isValidCollection() {
      method hasDuplicates (line 190) | public static boolean hasDuplicates() {
      method swap (line 228) | public static void swap(List<?> elements, int i, int j) {

FILE: 1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/util/BaseEntity.java
  class BaseEntity (line 10) | @Getter
    method BaseEntity (line 17) | public BaseEntity(UUID uuid) {

FILE: 1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java
  class CrazyGenericsTest (line 35) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method sourcedClassHasOneTypeParameter (line 41) | @Test
    method sourcedClassTypeParameterIsCalledT (line 50) | @Test
    method sourcedClassTypeParameterIsNotBounded (line 59) | @Test
    method valueFieldIsGeneric (line 69) | @Test
    method limitedClassHasOneTypeParameter (line 81) | @Test
    method limitedClassTypeParameterIsBoundedByNumber (line 90) | @Test
    method limitedClassFieldsAreGeneric (line 102) | @Test
    method converterInterfaceHasTwoTypeParameters (line 114) | @Test
    method converterInterfaceFirstTypeParameterIsCalledT (line 123) | @Test
    method converterInterfaceSecondTypeParameterIsCalledR (line 133) | @Test
    method converterMethodParameterHasTypeT (line 143) | @Test
    method converterMethodReturnTypeIsR (line 154) | @Test
    method maxHolderClassHasOneTypeParameter (line 164) | @Test
    method maxHolderClassTypeParameterIsCalledT (line 173) | @Test
    method maxHolderClassTypeParameterShouldBeBoundByComparableT (line 182) | @Test
    method maxHolderFieldHasTypeT (line 194) | @Test
    method maxHolderConstructorParamHasTypeT (line 203) | @Test
    method maxHolderPutMethodParamHasTypeT (line 215) | @Test
    method maxHolderGetMaxReturnTypeIsT (line 226) | @Test
    method maxHolderPut (line 236) | @Test
    method maxHolderPutWhenArgumentIsLessThanMax (line 251) | @Test
    method strictProcessorHasOneGenericType (line 266) | @Test
    method strictProcessorTypeParameterIsCalledT (line 275) | @Test
    method strictProcessorTypeParameterIsBoundBySerializableAndComparable (line 285) | @Test
    method strictProcessorProcessMethodParameterHasTypeT (line 298) | @Test
    method collectionRepositoryHasTwoTypeParameters (line 310) | @Test
    method collectionRepositoryFirstTypeParameterIsCalledT (line 319) | @Test
    method collectionRepositoryFirstTypeParameterIsBoundedByBaseEntity (line 328) | @Test
    method collectionRepositorySecondTypeParameterIsCalledT (line 338) | @Test
    method collectionRepositorySecondTypeParameterIsBoundedByCollection (line 347) | @Test
    method collectionRepositorySaveMethodParameterHasTypeT (line 358) | @Test
    method collectionRepositoryGetCollectionMethodHasReturnTypeC (line 370) | @Test
    method listRepositoryExtendsCollectionRepository (line 380) | @Test
    method listRepositoryHasOneTypeParameter (line 389) | @Test
    method listRepositoryTypeParameterIsCalledT (line 398) | @Test
    method listRepositoryTypeParameterIsBoundByBaseEntity (line 407) | @Test
    method listRepositoryParentInterfaceHasSpecifiedTwoTypes (line 417) | @Test
    method comparableCollectionIsGeneric (line 427) | @Test
    method comparableCollectionExtendsCollection (line 437) | @Test
    method comparableCollectionExtendsCollectionOfTheSameElementsType (line 449) | @Test
    method comparableCollectionExtendsComparable (line 463) | @Test
    method comparableCollectionExtendsComparableOfCollectionOfAnyType (line 475) | @Test
    method comparableCollectionOverridesCompareToMethod (line 489) | @Test
    method compareToProvidesDefaultImpl (line 500) | @Test
    method compareToParamIsACollectionOfAnyType (line 509) | @Test
    method compareToComparesSize (line 520) | @Order(46)
    method collectionUtilIsNotAGenericClass (line 537) | @Test
    method printParamIsAListOfAnyType (line 546) | @Test
    method hasNewEntitiesMethodParamIsAGenericCollectionOfEntities (line 558) | @Test
    method hasNewEntitiesChecksIds (line 570) | @ParameterizedTest
    method getMethodByName (line 583) | private Method getMethodByName(Class<?> clazz, String methodName) {
    method hasNewEntitiesArgs (line 589) | static Stream<Arguments> hasNewEntitiesArgs() {
    method isValidCollectionMethodFirstParamIsAGenericCollectionOfEntities (line 602) | @Test
    method isValidCollectionMethodSecondParamIsAnyBaseEntitySuperClass (line 614) | @Test
    method isValidCollectionReturnsTrueWhenAllEntitiesPassValidationPredicate (line 626) | @ParameterizedTest
    method isValidCollectionArgs (line 641) | private static Stream<Arguments> isValidCollectionArgs() {
    method hasDuplicatesIsAGenericMethod (line 669) | @Test
    method hasDuplicatesTypeParameterIsCalledT (line 678) | @Test
    method hasDuplicatesTypeParameterIsBoundedByBaseEntity (line 688) | @Test
    method hasDuplicatesChecksEntitiesByUUID (line 699) | @ParameterizedTest
    method hasDuplicatesArgs (line 712) | static Stream<Arguments> hasDuplicatesArgs() {
    method findMaxHasPublicAccessType (line 725) | @Test
    method findMaxMethodReturnMaxValue (line 733) | @ParameterizedTest
    method findMaxArgs (line 746) | static Stream<Arguments> findMaxArgs() {
    method findMostRecentlyCreatedEntityHasPublicAccessType (line 764) | @Test
    method findMostRecentlyCreatedEntityIsAGenericMethod (line 772) | @Test
    method findMostRecentlyCreatedEntityReturnsEntityWithMaxCreatedOnValue (line 784) | @ParameterizedTest
    method findMostRecentlyCreatedEntityArgs (line 798) | static Stream<Arguments> findMostRecentlyCreatedEntityArgs() {
    method findMostRecentlyCreatedEntityThrowsException (line 811) | @Test
    method swapMethodDoesNotDeclareTypeParameter (line 822) | @Test
    method swapChangesElements (line 832) | @Test
    class TestEntity (line 848) | static class TestEntity extends BaseEntity {
      method TestEntity (line 849) | public TestEntity() {
      method TestEntity (line 853) | public TestEntity(UUID uuid) {
      method TestEntity (line 857) | public TestEntity(LocalDateTime createdOn) {

FILE: 1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java
  class HeterogeneousMaxHolder (line 17) | public class HeterogeneousMaxHolder {

FILE: 1-0-java-basics/1-3-2-heterogeneous-max-holder/src/test/java/com/bobocode/basics/HeterogeneousMaxHolderTest.java
  class HeterogeneousMaxHolderTest (line 24) | @TestMethodOrder(OrderAnnotation.class)
    method classDoesNotDeclareTypeParameters (line 29) | @Test
    method classHasOneField (line 38) | @Test
    method mapFieldStoresTypeToObject (line 48) | @Test
    method putExists (line 59) | @Test
    method putDeclaresOneTypeParam (line 69) | @Test
    method putMethodAcceptsTypeSafeKeyParameter (line 82) | @Test
    method putMethodAcceptsComparableValueParameter (line 95) | @Test
    method putMethodAcceptsValueParameterWithComparableSuperClass (line 107) | @Test
    method putStoresValueWhenCurrentMaxIsNull (line 121) | @Test
    method putReturnsNullWhenCurrentMaxIsNull (line 132) | @Test
    method putStoresValueWhenCurrentMaxIsSmaller (line 142) | @Test
    method putReturnsOldMaxValue (line 155) | @Test
    method putIgnoresNewValueWhenCurrentMaxIsGreater (line 167) | @Test
    method putReturnsProvidedValueWhenCurrentMaxIsGreater (line 180) | @Test
    method putIsOverloadedWithAdditionalComparatorParam (line 192) | @Test
    method overloadedPutDeclaresOneTypeParam (line 200) | @Test
    method overloadedPutMethodAcceptsTypeSafeKeyParameter (line 213) | @Test
    method overloadedPutMethodAcceptsAnyValue (line 226) | @Test
    method overloadedPutAcceptsComparatorOfSuperTypes (line 239) | @Test
    method overloadedPutStoresValueWhenCurrentMaxIsNull (line 252) | @Test
    method overloadedPutReturnsNullWhenCurrentMaxIsNull (line 264) | @Test
    method overloadedPutStoresValueWhenCurrentMaxIsSmaller (line 274) | @Test
    method overloadedPutReturnsOldMaxValue (line 290) | @Test
    method overloadedPutIgnoresNewValueWhenCurrentMaxIsGreater (line 305) | @Test
    method overloadedPutReturnsProvidedValueWhenCurrentMaxIsGreater (line 321) | @Test
    method getMaxExists (line 336) | @Test
    method getMaxDeclaresOneTypeParam (line 346) | @Test
    method getMaxHasOneParameter (line 358) | @Test
    method getMaxParamSpecifyTypeArguments (line 370) | @Test
    method getMaxReturnsValueWhenItExists (line 381) | @Test
    method getMaxReturnsNullWhenNoValueByGivenTypeExists (line 392) | @Test
    method maxHolderKeepsTrackOfMultipleValuesPerType (line 401) | @Test
    method callGetMax (line 419) | @SneakyThrows
    method getGetMaxMethod (line 426) | private Method getGetMaxMethod() {
    method getPutMethod (line 433) | private Method getPutMethod() {
    method getOverloadedPutMethod (line 441) | private Method getOverloadedPutMethod() {
    method givenMaxHolderWithData (line 449) | private void givenMaxHolderWithData(Class<?> type, Object value) {
    method callPut (line 455) | @SneakyThrows
    method callPut (line 462) | @SneakyThrows
    method getMaxHelper (line 469) | @SneakyThrows
    method getInternalMap (line 475) | @SneakyThrows

FILE: 1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java
  class HelloAnnotationsExercise (line 16) | public class HelloAnnotationsExercise { // todo: mark class with the ann...

FILE: 1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/Level.java
  type Level (line 6) | public enum Level {

FILE: 1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsExerciseTest.java
  class HelloAnnotationsExerciseTest (line 12) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method exerciseAnnotationExists (line 15) | @Test
    method exerciseAnnotationCanBeAppliedForClasses (line 22) | @Test
    method exerciseAnnotationInfoIsAccessibleAtRuntime (line 35) | @Test
    method exerciseAnnotationHasValueDeclared (line 47) | @Test
    method exerciseAnnotationHasComplexityDeclared (line 58) | @Test
    method exerciseAnnotationComplexityLevelDefaultValue (line 69) | @Test
    method helloAnnotationExerciseIsAnnotatedWithExercise (line 81) | @Test
    method getExerciseAnnotation (line 95) | @SneakyThrows

FILE: 2-0-data-structures-and-algorithms/2-2-1-node/src/main/java/com/bobobode/cs/Node.java
  class Node (line 11) | public class Node<T> {

FILE: 2-0-data-structures-and-algorithms/2-2-1-node/src/main/java/com/bobobode/cs/Nodes.java
  class Nodes (line 13) | public class Nodes {
    method Nodes (line 14) | private Nodes() {
    method create (line 24) | public static <T> Node<T> create(T element) {
    method link (line 35) | public static <T> void link(Node<T> first, Node<T> second) {
    method pairOf (line 48) | public static <T> Node<T> pairOf(T firstElement, T secondElement) {
    method closedPairOf (line 62) | public static <T> Node<T> closedPairOf(T firstElement, T secondElement) {
    method chainOf (line 74) | public static <T> Node<T> chainOf(T... elements) {
    method circleOf (line 87) | public static <T> Node<T> circleOf(T... elements) {

FILE: 2-0-data-structures-and-algorithms/2-2-1-node/src/test/java/com/bobocode/cs/NodesTest.java
  class NodesTest (line 17) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method create (line 20) | @Test
    method link (line 31) | @Test
    method pair (line 45) | @Test
    method closedPair (line 59) | @Test
    method chain (line 73) | @Test
    method circle (line 93) | @Test
    method createNodeOf (line 113) | @SneakyThrows
    method getNodeElement (line 130) | @SneakyThrows
    method setNodeElement (line 137) | @SneakyThrows
    method getNodeNext (line 143) | @SneakyThrows
    method setNodeNext (line 150) | @SneakyThrows
    method getAccessibleElementField (line 156) | private Field getAccessibleElementField() {
    method getAccessibleNextField (line 165) | private Field getAccessibleNextField() {

FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/LinkedStack.java
  class LinkedStack (line 17) | public class LinkedStack<T> implements Stack<T> {
    method of (line 26) | public static <T> LinkedStack<T> of(T... elements) {
    method push (line 36) | @Override
    method pop (line 48) | @Override
    method size (line 58) | @Override
    method isEmpty (line 68) | @Override

FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/Stack.java
  type Stack (line 12) | public interface Stack<T> {
    method push (line 14) | void push(T element);
    method pop (line 16) | T pop();
    method size (line 18) | int size();
    method isEmpty (line 20) | boolean isEmpty();

FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/exception/EmptyStackException.java
  class EmptyStackException (line 3) | public class EmptyStackException extends RuntimeException{

FILE: 2-0-data-structures-and-algorithms/2-2-2-stack/src/test/java/com/bobocode/cs/LinkedStackTest.java
  class LinkedStackTest (line 24) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method checkProperInnerClassName (line 47) | @Test
    method noteIsAGenericClass (line 55) | @Test
    method checkProperHeadFieldName (line 64) | @Test
    method checkProperSizeFieldName (line 76) | @Test
    method checkProperElementField (line 88) | @Test
    method checkProperNextField (line 104) | @Test
    method of (line 116) | @Test
    method push (line 127) | @Test
    method pushAddsElementWhenStackIsEmpty (line 136) | @Test
    method pushAddsElementToHeadWhenStackIsEmpty (line 145) | @Test
    method pushAddsElementToHeadWhenStackIsNotEmpty (line 157) | @Test
    method pushPutsHeadToNextOfNewHead (line 171) | @Test
    method pushThrowsExceptionWhenElementIsNull (line 188) | @Test
    method popElementWhenStackIsEmpty (line 195) | @Test
    method pop (line 202) | @Test
    method popResetsHeadFromNextOfOldHead (line 213) | @Test
    method sizeWhenStackIsEmpty (line 228) | @Test
    method size (line 237) | @Test
    method sizeIncreasesWhenUseOfMethod (line 246) | @Test
    method sizeIncreasesWhenPush (line 255) | @Test
    method sizeDecreasesWhenPop (line 266) | @Test
    method isEmpty (line 276) | @Test
    method isEmptyWhenStackIsEmpty (line 287) | @Test
    method getInnerClass (line 295) | private Class<?> getInnerClass() {
    method getHeadField (line 301) | private Field getHeadField() {
    method getNodeElementField (line 310) | private Field getNodeElementField(Object node) {
    method getNodeNextField (line 319) | private Field getNodeNextField(Object node) {
    method getHeadObject (line 328) | @SneakyThrows
    method getNodeElementInt (line 333) | @SneakyThrows
    method getNodeNextObject (line 338) | @SneakyThrows
    method contains (line 343) | private boolean contains(int element) {
    method checkNext (line 356) | private boolean checkNext(Object node, int element) {
    method newNode (line 364) | @SneakyThrows
    method fillTestStack (line 380) | private void fillTestStack(int... elements) {
    method addToStack (line 387) | @SneakyThrows
    method getInnerSizeField (line 396) | private Field getInnerSizeField() {
    method setInnerSize (line 405) | @SneakyThrows
    method getInnerSize (line 410) | @SneakyThrows

FILE: 2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/main/java/com/bobocode/cs/LinkedQueue.java
  class LinkedQueue (line 17) | public class LinkedQueue<T> implements Queue<T> {
    method add (line 24) | public void add(T element) {
    method poll (line 33) | public T poll() {
    method size (line 42) | public int size() {
    method isEmpty (line 51) | public boolean isEmpty() {

FILE: 2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/main/java/com/bobocode/cs/Queue.java
  type Queue (line 7) | public interface Queue<T> {
    method add (line 13) | void add(T element);
    method poll (line 20) | T poll();
    method size (line 27) | int size();
    method isEmpty (line 34) | boolean isEmpty();

FILE: 2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/test/java/com/bobocode/cs/LinkedQueueTest.java
  class LinkedQueueTest (line 26) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method createNodeClass (line 54) | @Test
    method checkFieldsNameInNodeClass (line 63) | @Test
    method checkFieldsInQueueCLass (line 74) | @Test
    method addFillsQueueWhenItIsEmpty (line 87) | @Test
    method addFillsQueueWhenItIsNotEmpty (line 103) | @Test
    method addIncreasesQueueSize (line 122) | @Test
    method pollReturnsNullWhenQueueIsEmpty (line 132) | @Test
    method pollDeletesElementFromHead (line 140) | @Test
    method pollDecreasesQueueSize (line 154) | @Test
    method pollMakesSizeZeroWhenQueueHasSingleElement (line 165) | @Test
    method pollMakesQueueEmptyWhenQueueHasSingleElement (line 176) | @Test
    method sizeReturnsZeroWhenQueueIsEmpty (line 192) | @Test
    method size (line 200) | @Test
    method isEmptyReturnsTrueWhenQueueIsEmpty (line 209) | @Test
    method isEmpty (line 217) | @Test
    method getInnerStaticNodeClass (line 227) | private Class<?> getInnerStaticNodeClass() {
    method hasField (line 234) | private boolean hasField(Class<?> classToSearch, Predicate targetField) {
    method getInternalSize (line 239) | @SneakyThrows
    method pollElementFromQueue (line 246) | @SneakyThrows
    method addIntElementToQueue (line 286) | @SneakyThrows
    method createNode (line 309) | @SneakyThrows
    method isEmptyQueue (line 328) | @SneakyThrows
    method setInternalSize (line 336) | @SneakyThrows
    method setHead (line 343) | @SneakyThrows
    method setTail (line 349) | @SneakyThrows
    method setNextNode (line 355) | @SneakyThrows
    method getAccessibleFieldByPredicate (line 361) | private Field getAccessibleFieldByPredicate(Object object, Predicate<F...
    method getNodeValue (line 370) | @SneakyThrows

FILE: 2-0-data-structures-and-algorithms/2-2-4-linked-list/src/main/java/com/bobocode/cs/LinkedList.java
  class LinkedList (line 17) | public class LinkedList<T> implements List<T> {
    method of (line 26) | public static <T> LinkedList<T> of(T... elements) {
    method add (line 35) | @Override
    method add (line 47) | @Override
    method set (line 59) | @Override
    method get (line 71) | @Override
    method getFirst (line 82) | @Override
    method getLast (line 93) | @Override
    method remove (line 105) | @Override
    method contains (line 116) | @Override
    method isEmpty (line 126) | @Override
    method size (line 136) | @Override
    method clear (line 144) | @Override

FILE: 2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java
  class LinkedListTest (line 28) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method properNodeName (line 50) | @Test
    method properNodeFields (line 60) | @Test
    method getInnerClass (line 75) | private Class<?> getInnerClass() {
    method addIntoEmptyList (line 82) | @Test
    method addIntoEmptyListChangesSize (line 92) | @Test
    method add (line 102) | @Test
    method addChangesSize (line 118) | @Test
    method addByIndex (line 130) | @Test
    method addByZeroIndexWhenListIsEmpty (line 149) | @Test
    method addByIndexToTheEndOfList (line 161) | @Test
    method addToHeadWhenListIsNotEmpty (line 173) | @Test
    method addThrowsExceptionWhenIndexIsNegative (line 189) | @Test
    method addThrowsExceptionWhenIndexLargerThanSize (line 196) | @Test
    method addWhenIndexEqualToSize (line 207) | @Test
    method of (line 221) | @Test
    method ofChangeSize (line 231) | @Test
    method setByIndex (line 241) | @Test
    method setFirstElementWhenListIsEmpty (line 260) | @Test
    method setByIndexEqualToSize (line 267) | @Test
    method get (line 276) | @Test
    method getByZeroIndexWhenListHasSingleElement (line 291) | @Test
    method getByZeroIndexWhenListIsEmpty (line 301) | @Test
    method getWhenIndexIsNegative (line 308) | @Test
    method getWhenIndexIsEqualToListSize (line 315) | @Test
    method getFirst (line 324) | @Test
    method getLast (line 334) | @Test
    method getFirstWhenListIsEmpty (line 344) | @Test
    method getLastWhenListIsEmpty (line 351) | @Test
    method remove (line 359) | @Test
    method removeChangesSize (line 373) | @Test
    method removeFirst (line 387) | @Test
    method removeLast (line 402) | @Test
    method removeWhenListIsEmpty (line 420) | @Test
    method removeByZeroIndexWhenListIsEmpty (line 427) | @Test
    method size (line 434) | @Test
    method sizeWhenListIsEmpty (line 444) | @Test
    method contains (line 452) | @Test
    method containsWhenListIsEmpty (line 464) | @Test
    method isEmpty (line 472) | @Test
    method isEmptyWhenListIsEmpty (line 482) | @Test
    method clearWhenListIsEmpty (line 490) | @Test
    method clearChangesSize (line 500) | @Test
    method clearRemovesAllElements (line 512) | @Test
    method getInternalElement (line 523) | @SneakyThrows
    method getInternalSize (line 534) | @SneakyThrows
    method addInternalElements (line 539) | @SneakyThrows
    method getInternalHeadField (line 559) | private Field getInternalHeadField() {
    method getInternalTailField (line 563) | private Field getInternalTailField() {
    method setInternalSize (line 567) | @SneakyThrows
    method createNodeObjectWithInternalElement (line 573) | @SneakyThrows
    method createNodeByConstructorWithoutParameters (line 589) | @SneakyThrows
    method getAccessibleFieldByPredicate (line 598) | private Field getAccessibleFieldByPredicate(Object object, Predicate<F...
    method getNodeValue (line 607) | @SneakyThrows

FILE: 2-0-data-structures-and-algorithms/2-2-5-array-list/src/main/java/com/bobocode/cs/ArrayList.java
  class ArrayList (line 14) | public class ArrayList<T> implements List<T> {
    method ArrayList (line 22) | public ArrayList(int initCapacity) {
    method ArrayList (line 30) | public ArrayList() {
    method of (line 40) | public static <T> List<T> of(T... elements) {
    method add (line 49) | @Override
    method add (line 60) | @Override
    method get (line 72) | @Override
    method getFirst (line 83) | @Override
    method getLast (line 94) | @Override
    method set (line 106) | @Override
    method remove (line 118) | @Override
    method contains (line 129) | @Override
    method isEmpty (line 139) | @Override
    method size (line 147) | @Override
    method clear (line 155) | @Override

FILE: 2-0-data-structures-and-algorithms/2-2-5-array-list/src/test/java/com/bobocode/cs/ArrayListTest.java
  class ArrayListTest (line 23) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method add (line 28) | @Test
    method sizeOfEmptyArrayWrapper (line 42) | @Test
    method size (line 48) | @Test
    method getElementsByIndex (line 55) | @Test
    method getFirstElement (line 66) | @Test
    method getLastElement (line 73) | @Test
    method getFirstOfEmptyList (line 80) | @Test
    method getLastOfEmptyList (line 87) | @Test
    method createListWithSpecificArrayCapacity (line 94) | @Test
    method createListWithWrongCapacity (line 101) | @Test
    method addElements (line 108) | @Test
    method addShouldResizeDefaultCapacityWhenArrayIsFull (line 120) | @Test
    method addShouldResizeSpecificCapacityWhenArrayIsFull (line 138) | @Test
    method addElementByIndex (line 155) | @Test
    method addElementByNegativeIndex (line 170) | @Test
    method addElementByIndexLargerThanListSize (line 177) | @Test
    method addElementByIndexEqualToSize (line 185) | @Test
    method getFirstElementFromEmptyList (line 198) | @Test
    method getElementByNegativeIndex (line 205) | @Test
    method getElementByIndexThrowsExceptionWhenIndexIsOutOfBound (line 212) | @Test
    method setElementByIndex (line 220) | @Test
    method setElementByIndexThrowsExceptionWhenIndexIsOutOfBound (line 233) | @Test
    method setFirstElementOnEmptyList (line 241) | @Test
    method removeElementByIndex (line 248) | @Test
    method removeElementByIndexThrowsExceptionWhenIndexEqualsSize (line 262) | @Test
    method removeLastElementByIndex (line 270) | @Test
    method removeElementByIndexThrowsExceptionWhenIndexIsOutOfBounds (line 283) | @Test
    method containsOnEmptyList (line 292) | @Test
    method containsElement (line 298) | @Test
    method containsNotExistingWhenArrayIsNotFilled (line 305) | @Test
    method findNotExistingElement (line 318) | @Test
    method isEmptyOnEmptyList (line 325) | @Test
    method isEmpty (line 331) | @Test
    method clearOnEmptyList (line 338) | @Test
    method clearChangesTheSize (line 344) | @Test
    method clearRemovesElements (line 353) | @Test
    method setTestSize (line 364) | @SneakyThrows
    method getTestSize (line 371) | @SneakyThrows
    method getTestArray (line 378) | @SneakyThrows
    method getTestArrayName (line 385) | private String getTestArrayName() {
    method fillTestArray (line 397) | @SneakyThrows

FILE: 2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/main/java/com/bobocode/cs/BinarySearchTree.java
  type BinarySearchTree (line 5) | public interface BinarySearchTree<T extends Comparable<T>> {
    method insert (line 10) | boolean insert(T element);
    method contains (line 15) | boolean contains(T element);
    method size (line 20) | int size();
    method depth (line 25) | int depth();
    method inOrderTraversal (line 31) | void inOrderTraversal(Consumer<T> consumer);

FILE: 2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/main/java/com/bobocode/cs/RecursiveBinarySearchTree.java
  class RecursiveBinarySearchTree (line 19) | public class RecursiveBinarySearchTree<T extends Comparable<T>> implemen...
    method of (line 21) | public static <T extends Comparable<T>> RecursiveBinarySearchTree<T> o...
    method insert (line 25) | @Override
    method contains (line 30) | @Override
    method size (line 35) | @Override
    method depth (line 40) | @Override
    method inOrderTraversal (line 45) | @Override

FILE: 2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/test/java/com/bobocode/cs/RecursiveBinarySearchTreeTest.java
  class RecursiveBinarySearchTreeTest (line 32) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method properNodeClassNameCheck (line 57) | @Test
    method properTreeFieldsCheck (line 66) | @Test
    method properNodeFieldsCheck (line 81) | @Test
    method of (line 100) | @Test
    method insert (line 110) | @Test
    method insertToRootIfTreeIsEmpty (line 120) | @Test
    method insertLeftIfLessThanRoot (line 128) | @Test
    method insertRightIfGreaterThanRoot (line 137) | @Test
    method insertDoesNotAddDuplicateElements (line 146) | @Test
    method insertThrowsExceptionWhenArgumentIsNull (line 156) | @Test
    method containsReturnsTrueIfElementExist (line 165) | @Test
    method containsReturnsFalseIfElementDoesntExist (line 175) | @Test
    method containsThrowsExceptionIFParameterIsNull (line 182) | @Test
    method sizeIsGrowingWhenInserting (line 188) | @Test
    method sizeDoesNotGrowWhenInsertingNotUnique (line 198) | @Test
    method depth (line 212) | @Order(16)
    method depthGrowWhenInsert (line 220) | @Test
    method depthIsZeroIfRootIsNull (line 233) | @Test
    method inorderTraversal (line 239) | @Test
    method depthArguments (line 252) | public static Stream<Arguments> depthArguments() {
    method getInnerSize (line 293) | @SneakyThrows
    method getInnerSizeField (line 298) | private Field getInnerSizeField() {
    method getInnerClass (line 307) | private Class<?> getInnerClass() {
    method getRootField (line 314) | @SneakyThrows
    method getRootObject (line 324) | @SneakyThrows
    method contains (line 334) | @SneakyThrows
    method findNodeByElement (line 339) | private Object findNodeByElement(Object node, int element) {
    method getNodesField (line 354) | @SneakyThrows
    method getElement (line 364) | @SneakyThrows
    method getLeftNode (line 369) | @SneakyThrows
    method getRightNode (line 374) | @SneakyThrows
    method newNode (line 379) | @SneakyThrows
    method insertElement (line 397) | @SneakyThrows
    method insertToSubtree (line 409) | private boolean insertToSubtree(Object node, int element) {
    method insertLeft (line 419) | @SneakyThrows
    method insertRight (line 429) | @SneakyThrows
    method fillTestTree (line 439) | @SneakyThrows

FILE: 2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/HashTable.java
  class HashTable (line 29) | public class HashTable<K, V> implements Map<K, V> {
    method calculateIndex (line 45) | public static int calculateIndex(Object key, int tableCapacity) {
    method put (line 60) | @Override
    method get (line 72) | @Override
    method containsKey (line 83) | @Override
    method containsValue (line 94) | @Override
    method size (line 104) | @Override
    method isEmpty (line 114) | @Override
    method remove (line 125) | @Override
    method toString (line 151) | @Override
    method resizeTable (line 169) | public void resizeTable(int newCapacity) {

FILE: 2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/Map.java
  type Map (line 13) | public interface Map<K, V> {
    method put (line 22) | V put(K key, V value);
    method get (line 30) | V get(K key);
    method containsKey (line 38) | boolean containsKey(K key);
    method containsValue (line 46) | boolean containsValue(V value);
    method size (line 53) | int size();
    method isEmpty (line 60) | boolean isEmpty();
    method remove (line 68) | V remove(K key);

FILE: 2-0-data-structures-and-algorithms/2-2-9-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java
  class HashTableTest (line 26) | @TestClassOrder(OrderAnnotation.class)
    class NodeClassTest (line 32) | @Nested
      method nodeClassExists (line 38) | @Test
      method nodeIsStaticClass (line 49) | @Test
      method nodeHasTwoTypeParams (line 59) | @Test
      method nodeHasKeyValuesFields (line 70) | @Test
      method nodeHasReferenceToNext (line 83) | @Test
      method nodeHasOneConstructor (line 96) | @Test
      method nodeConstructorAcceptKeyValue (line 108) | @Test
    class HashTableFieldsTest (line 123) | @Nested
      method tableFieldExists (line 129) | @Test
      method sizeFieldExists (line 142) | @Test
    class HashTableConstructorsTest (line 153) | @Nested
      method defaultConstructor (line 159) | @Test
      method constructorWithTableCapacity (line 172) | @Test
      method constructorWithTableCapacityWhenArgumentIsNegative (line 185) | @Test
    class HashFunctionTest (line 197) | @Nested
      method calculateIndexReturnTheSameValueWhenKeyIsTheSame (line 203) | @Test
      method calculateIndexReturnDifferentValuesWheKeysAreDifferent (line 215) | @Test
      method calculateIndexReturnIndexInArrayBounds (line 228) | @Test
      method calculateIndexReturnPositiveIndexWhenHashCodeIsNegative (line 246) | @Test
    class HashTableMethodsTest (line 258) | @Nested
      method putWhenTableIsEmpty (line 264) | @Test
      method putTwoElementsWithTheSameHashCode (line 278) | @Test
      method putElementWithTheSameKey (line 299) | @Test
      method getElementWhenKeyDoesNotExists (line 314) | @Test
      method getWhenKeyExists (line 323) | @Test
      method getWhenOtherKeyHaveTheSameIndex (line 334) | @Test
      method containsKeyWhenElementExists (line 347) | @Test
      method containsKeyWhenElementDoesNotExist (line 358) | @Test
      method containsValue (line 367) | @Test
      method containsValueWhenItDoesNotExist (line 378) | @Test
      method containsValueWhenValueAppearsMultipleTimes (line 389) | @Test
      method size (line 402) | @Test
      method isEmptyWhenTableGetsElements (line 414) | @Test
      method isEmptyWhenThereIsNoElements (line 426) | @Test
      method remove (line 435) | @Test
      method removeWhenKeyDoesNotExists (line 448) | @Test
      method removeFromTheMiddleOfTheList (line 457) | @Test
      method removeFromTheEndOfTheList (line 476) | @Test
    class HashTableHelperMethodsTest (line 496) | @Nested
      method resizeTable (line 502) | @Test
      method toStringTest (line 520) | @Test
    method getInternalTable (line 538) | @SneakyThrows
    method addToTable (line 545) | private void addToTable(String key, Integer value) {
    method getNodeByKey (line 564) | private NodeProxy getNodeByKey(Object key) {
    method checkKeyValueExists (line 580) | private boolean checkKeyValueExists(Object key, Object value) {
    method checkKeyExists (line 585) | private boolean checkKeyExists(Object key) {
    method setSize (line 590) | @SneakyThrows
    method getSize (line 597) | @SneakyThrows
    method tableToString (line 604) | private String tableToString(Object[] table) {
    method createNewNode (line 622) | @SneakyThrows
    class NodeProxy (line 629) | static class NodeProxy {
      method NodeProxy (line 637) | @SneakyThrows
      method key (line 650) | @SneakyThrows
      method value (line 655) | @SneakyThrows
      method next (line 660) | @SneakyThrows
      method setValue (line 667) | @SneakyThrows
      method setNext (line 672) | @SneakyThrows

FILE: 2-0-data-structures-and-algorithms/data-structures-and-algorithms-util/src/main/java/com/bobocode/cs/List.java
  type List (line 4) | public interface List<T> {
    method add (line 5) | void add(T element);
    method add (line 7) | void add(int index, T element);
    method set (line 9) | void set(int index, T element);
    method get (line 11) | T get(int index);
    method getFirst (line 13) | T getFirst();
    method getLast (line 15) | T getLast();
    method remove (line 17) | T remove(int index);
    method contains (line 19) | boolean contains(T element);
    method isEmpty (line 21) | boolean isEmpty();
    method size (line 23) | int size();
    method clear (line 25) | void clear();

FILE: 3-0-java-core/3-6-1-file-reader/src/main/java/com/bobocode/se/FileReaders.java
  class FileReaders (line 8) | public class FileReaders {
    method readWholeFile (line 16) | public static String readWholeFile(String fileName) {

FILE: 3-0-java-core/3-6-1-file-reader/src/test/java/com/bobocode/se/FileReadersTest.java
  class FileReadersTest (line 7) | public class FileReadersTest {
    method testReadWholeFileOnEmptyFile (line 9) | @Test
    method testReadWholeFileOnFileWithEmptyLines (line 17) | @Test
    method testReadWholeFile (line 28) | @Test

FILE: 3-0-java-core/3-6-2-file-stats/src/main/java/com/bobocode/se/FileStats.java
  class FileStats (line 9) | public class FileStats {
    method from (line 16) | public static FileStats from(String fileName) {
    method getCharCount (line 26) | public int getCharCount(char character) {
    method getMostPopularCharacter (line 35) | public char getMostPopularCharacter() {
    method containsCharacter (line 45) | public boolean containsCharacter(char character) {

FILE: 3-0-java-core/3-6-2-file-stats/src/main/java/com/bobocode/se/FileStatsException.java
  class FileStatsException (line 3) | public class FileStatsException extends RuntimeException{
    method FileStatsException (line 4) | public FileStatsException(String message) {
    method FileStatsException (line 8) | public FileStatsException(String message, Throwable cause) {

FILE: 3-0-java-core/3-6-2-file-stats/src/test/java/com/bobocode/se/FileStatsTest.java
  class FileStatsTest (line 10) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method createFileStatsFromExistingFile (line 13) | @Test
    method createFileStatsFromNonExistingFile (line 19) | @Test
    method getCharCount (line 25) | @Test
    method getMostPopularCharacter (line 38) | @Test
    method containsCharacter (line 53) | @Test

FILE: 3-0-java-core/3-6-3-crazy-regex/src/main/java/com/bobocode/se/CrazyRegex.java
  class CrazyRegex (line 16) | public class CrazyRegex {
    method findSpecificWord (line 23) | public Pattern findSpecificWord() {
    method findFirstWord (line 32) | public Pattern findFirstWord() {
    method findLastWord (line 41) | public Pattern findLastWord() {
    method findAllNumbers (line 52) | public Pattern findAllNumbers() {
    method findDates (line 61) | public Pattern findDates() {
    method findDifferentSpellingsOfColor (line 71) | public Pattern findDifferentSpellingsOfColor() {
    method findZipCodes (line 82) | public Pattern findZipCodes() {
    method findDifferentSpellingsOfLink (line 92) | public Pattern findDifferentSpellingsOfLink() {
    method findSimplePhoneNumber (line 102) | public Pattern findSimplePhoneNumber() {
    method findNumbersFromZeroToFiveWithLengthThree (line 113) | public Pattern findNumbersFromZeroToFiveWithLengthThree() {
    method findAllWordsWithFiveLength (line 122) | public Pattern findAllWordsWithFiveLength() {
    method findAllLettersAndDigitsWithLengthThree (line 133) | public Pattern findAllLettersAndDigitsWithLengthThree() {
    method findAllWordsWhichBeginWithCapitalLetter (line 142) | public Pattern findAllWordsWhichBeginWithCapitalLetter() {
    method findAbbreviation (line 152) | public Pattern findAbbreviation() {
    method findAllOpenBraces (line 161) | public Pattern findAllOpenBraces() {
    method findOnlyResources (line 170) | public Pattern findOnlyResources() {
    method findOnlyLinksInNote (line 179) | public Pattern findOnlyLinksInNote() {
    method findOnlyLinksInJson (line 188) | public Pattern findOnlyLinksInJson() {
    method findAllEmails (line 197) | public Pattern findAllEmails() {
    method findAllPatternsForPhoneNumbers (line 209) | public Pattern findAllPatternsForPhoneNumbers() {
    method findOnlyDuplicates (line 218) | public Pattern findOnlyDuplicates() {
    method replaceFirstAndLastNames (line 229) | public String replaceFirstAndLastNames(String names) {
    method replaceLastSevenDigitsOfPhoneNumberToX (line 240) | public String replaceLastSevenDigitsOfPhoneNumberToX(String phones) {
    method insertLinksAndResourcesIntoHref (line 252) | public String insertLinksAndResourcesIntoHref(String links) {

FILE: 3-0-java-core/3-6-3-crazy-regex/src/test/java/com/bobocode/se/CrazyRegexTest.java
  class CrazyRegexTest (line 24) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method CrazyRegexTest (line 32) | public CrazyRegexTest() {
    method findSpecificWord (line 37) | @Test
    method findFirstWord (line 44) | @Test
    method findLastWord (line 51) | @Test
    method findAllNumbers (line 58) | @Test
    method findDates (line 66) | @Test
    method findDifferentSpellingsOfColor (line 74) | @Test
    method findZipCodes (line 81) | @Test
    method findDifferentSpellingsOfLink (line 88) | @Test
    method findSimplePhoneNumber (line 95) | @Test
    method findNumbersFromZeroToFiveWithLengthThree (line 102) | @Test
    method findAllWordsWithFiveLength (line 110) | @Test
    method findAllLettersAndDigitsWithLengthThree (line 117) | @Test
    method findAllWordsWhichBeginWithCapitalLetter (line 126) | @Test
    method findAbbreviation (line 134) | @Test
    method findAllOpenBraces (line 141) | @Test
    method findOnlyResources (line 148) | @Test
    method findOnlyLinksInNote (line 155) | @Test
    method findOnlyLinksInJson (line 162) | @Test
    method findAllEmails (line 173) | @Test
    method findAllPatternsForPhoneNumbers (line 181) | @Test
    method findOnlyDuplicates (line 188) | @Test
    method replaceFirstAndLastNames (line 196) | @Test
    method replaceLastSevenDigitsOfPhoneNumberToX (line 204) | @Test
    method insertLinksAndResourcesIntoHref (line 212) | @Test
    method regexChecker (line 226) | private String regexChecker(Pattern pattern, String str2WorkWith) {
    method readWholeFile (line 237) | @SneakyThrows

FILE: 3-0-java-core/3-6-4-random-field-comparator/src/main/java/com/bobocode/se/RandomFieldComparator.java
  class RandomFieldComparator (line 19) | public class RandomFieldComparator<T> implements Comparator<T> {
    method RandomFieldComparator (line 21) | public RandomFieldComparator(Class<T> targetType) {
    method compare (line 35) | @Override
    method getComparingFieldName (line 43) | public String getComparingFieldName() {
    method toString (line 53) | @Override

FILE: 3-0-java-core/3-6-4-random-field-comparator/src/test/java/com/bobocode/se/RandomFieldComparatorTest.java
  class RandomFieldComparatorTest (line 18) | @TestMethodOrder(OrderAnnotation.class)
    method classDoesNotApplyNullInConstructor (line 23) | @Test
    method constructorThrowsExceptionIfNoComparableFieldsInProvidedType (line 30) | @Test
    method compareWhenFirstParameterAreNull (line 38) | @Test
    method compareWhenBothFieldValuesIsNull (line 47) | @Test
    method compareWhenFieldValuesOfFirstObjectIsNull (line 57) | @Test
    method compareWhenFieldValuesOfSecondObjectIsNull (line 69) | @Test
    method compareWhenFieldValueOfFirstObjectIsGreater (line 81) | @Test
    method compareWhenFieldValueOfSecondObjectIsGreater (line 101) | @Test
    method compareWhenFieldValuesOfObjectsAreEqual (line 121) | @Test
    method comparePrimitivesWhenFieldValueOfFirstObjectIsGreater (line 141) | @Test
    method comparePrimitivesWhenFieldValuesOfObjectsAreEqual (line 161) | @Test
    method comparePrimitivesWhenFieldValueOfSecondObjectIsGreater (line 181) | @Test
    method getComparingFieldName (line 201) | @Test
    method toStringOverriding (line 212) | @Test
    method setFieldToCompare (line 224) | @SneakyThrows
    class EmptyClass (line 234) | private static class EmptyClass {
    class ClassWithNotComparableField (line 238) | @AllArgsConstructor
    class Account (line 244) | @NoArgsConstructor

FILE: 4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/data/FlightDao.java
  class FlightDao (line 15) | public class FlightDao {
    method register (line 24) | public boolean register(String flightNumber) {
    method findAll (line 33) | public Set<String> findAll() {

FILE: 4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/factory/FlightServiceFactory.java
  class FlightServiceFactory (line 11) | public class FlightServiceFactory {
    method creteFlightService (line 18) | public FlightService creteFlightService() {

FILE: 4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/service/FlightService.java
  class FlightService (line 13) | public class FlightService {
    method registerFlight (line 21) | public boolean registerFlight(String flightNumber) {
    method searchFlights (line 31) | public List<String> searchFlights(String query) {

FILE: 4-0-object-oriented-programming/4-3-1-flight-search/src/test/java/com/bobocode/oop/FlightServiceTest.java
  class FlightServiceTest (line 11) | public class FlightServiceTest {
    method testRegisterFlight (line 15) | @Test
    method testRegisterSameFlightTwice (line 22) | @Test
    method testSearchExistingFlightByFullNumber (line 31) | @Test
    method testSearchNonExistingFlight (line 44) | @Test
    method testSearchFlights (line 51) | @Test

FILE: 5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/FunctionMap.java
  class FunctionMap (line 15) | public class FunctionMap<T, R> {
    method FunctionMap (line 18) | FunctionMap() {
    method addFunction (line 22) | public void addFunction(String name, Function<T, R> function) {
    method getFunction (line 26) | public Function<T, R> getFunction(String name) {

FILE: 5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/Functions.java
  class Functions (line 14) | public class Functions {
    method Functions (line 15) | private Functions() {
    method intFunctionMap (line 28) | public static FunctionMap<Integer, Integer> intFunctionMap() {

FILE: 5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/exception/InvalidFunctionNameException.java
  class InvalidFunctionNameException (line 3) | public class InvalidFunctionNameException extends RuntimeException {
    method InvalidFunctionNameException (line 4) | public InvalidFunctionNameException(String functionName) {

FILE: 5-0-functional-programming/5-0-1-lambda-functions-map/src/test/java/com/bobocode/fp/FunctionsTest.java
  class FunctionsTest (line 9) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method init (line 13) | @BeforeEach
    method squareFunction (line 18) | @Test
    method absFunction (line 28) | @Test
    method incrementFunction (line 38) | @Test
    method destDecrementFunction (line 48) | @Test
    method signFunctionOnNegativeValue (line 58) | @Test
    method signFunctionOnPositiveValue (line 68) | @Test
    method signFunctionOnZero (line 78) | @Test

FILE: 5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/SumOfSquares.java
  class SumOfSquares (line 11) | public class SumOfSquares {
    method main (line 12) | public static void main(String[] args) {
    method calculateSumOfSquaresInRange (line 23) | static int calculateSumOfSquaresInRange(int startInclusive, int endInc...

FILE: 5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/exception/InvalidRangeException.java
  class InvalidRangeException (line 3) | public class InvalidRangeException extends RuntimeException {

FILE: 5-0-functional-programming/5-0-2-stream-sum-of-squares/src/test/java/com/bobocode/fp/SumOfSquaresTest.java
  class SumOfSquaresTest (line 17) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method calculateSumOfSquaresOfZero (line 20) | @Test
    method calculateSumOfSquaresOfOne (line 28) | @Test
    method calculateSumOfSquares (line 36) | @Test
    method calculateSumOfSquaresOnNegative (line 44) | @Test
    method calculateWithInvalidRange (line 52) | @Test

FILE: 5-0-functional-programming/5-1-1-crazy-lambdas/src/main/java/com/bobocode/fp/CrazyLambdas.java
  class CrazyLambdas (line 22) | public class CrazyLambdas {
    method helloSupplier (line 29) | public static Supplier<String> helloSupplier() {
    method isEmptyPredicate (line 38) | public static Predicate<String> isEmptyPredicate() {
    method stringMultiplier (line 48) | public static BiFunction<String, Integer, String> stringMultiplier() {
    method toDollarStringFunction (line 58) | public static Function<BigDecimal, String> toDollarStringFunction() {
    method lengthInRangePredicate (line 70) | public static Predicate<String> lengthInRangePredicate(int min, int ma...
    method randomIntSupplier (line 79) | public static IntSupplier randomIntSupplier() {
    method boundedRandomIntSupplier (line 89) | public static IntUnaryOperator boundedRandomIntSupplier() {
    method intSquareOperation (line 98) | public static IntUnaryOperator intSquareOperation() {
    method longSumOperation (line 107) | public static LongBinaryOperator longSumOperation() {
    method stringToIntConverter (line 116) | public static ToIntFunction<String> stringToIntConverter() {
    method nMultiplyFunctionSupplier (line 127) | public static Supplier<IntUnaryOperator> nMultiplyFunctionSupplier(int...
    method composeWithTrimFunction (line 136) | public static UnaryOperator<Function<String, String>> composeWithTrimF...
    method runningThreadSupplier (line 147) | public static Supplier<Thread> runningThreadSupplier(Runnable runnable) {
    method newThreadRunnableConsumer (line 156) | public static Consumer<Runnable> newThreadRunnableConsumer() {
    method runnableToThreadSupplierFunction (line 166) | public static Function<Runnable, Supplier<Thread>> runnableToThreadSup...
    method functionToConditionalFunction (line 179) | public static BiFunction<IntUnaryOperator, IntPredicate, IntUnaryOpera...
    method functionLoader (line 190) | public static BiFunction<Map<String, IntUnaryOperator>, String, IntUna...
    method comparing (line 208) | public static <T, U extends Comparable<? super U>> Comparator<T> compa...
    method thenComparing (line 227) | public static <T, U extends Comparable<? super U>> Comparator<T> thenC...
    method trickyWellDoneSupplier (line 237) | public static Supplier<Supplier<Supplier<String>>> trickyWellDoneSuppl...

FILE: 5-0-functional-programming/5-1-1-crazy-lambdas/src/test/java/com/bobocode/fp/CrazyLambdasTest.java
  class CrazyLambdasTest (line 21) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method helloSupplier (line 24) | @Test
    method isEmptyPredicate (line 33) | @Test
    method stringMultiplier (line 45) | @Test
    method toDollarStringFunction (line 57) | @Test
    method lengthInRangePredicate (line 66) | @Test
    method randomIntSupplier (line 84) | @Test
    method boundedRandomIntSupplier (line 95) | @Test
    method intSquareOperation (line 111) | @Test
    method longSumOperation (line 123) | @Test
    method stringToIntConverter (line 138) | @Test
    method nMultiplyFunctionSupplier (line 150) | @Test
    method composeWithTrimFunction (line 161) | @Test
    method runningThreadSupplier (line 175) | @Test
    method newThreadRunnableConsumer (line 191) | @Test
    method runnableToThreadSupplierFunction (line 205) | @Test
    method functionToConditionalFunction (line 222) | @Test
    method functionLoader (line 235) | @Test
    method comparing (line 252) | @Test
    method thenComparing (line 263) | @Test
    method trickyWellDoneSupplier (line 275) | @Test

FILE: 5-0-functional-programming/5-2-1-crazy-streams/src/main/java/com/bobocode/fp/CrazyStreams.java
  class CrazyStreams (line 23) | @AllArgsConstructor
    method findRichestPerson (line 32) | public Optional<Account> findRichestPerson() {
    method findAccountsByBirthdayMonth (line 42) | public List<Account> findAccountsByBirthdayMonth(Month birthdayMonth) {
    method partitionMaleAccounts (line 52) | public Map<Boolean, List<Account>> partitionMaleAccounts() {
    method groupAccountsByEmailDomain (line 62) | public Map<String, List<Account>> groupAccountsByEmailDomain() {
    method getNumOfLettersInFirstAndLastNames (line 71) | public int getNumOfLettersInFirstAndLastNames() {
    method calculateTotalBalance (line 80) | public BigDecimal calculateTotalBalance() {
    method sortByFirstAndLastNames (line 89) | public List<Account> sortByFirstAndLastNames() {
    method containsAccountWithEmailDomain (line 99) | public boolean containsAccountWithEmailDomain(String emailDomain) {
    method getBalanceByEmail (line 110) | public BigDecimal getBalanceByEmail(String email) {
    method collectAccountsById (line 119) | public Map<Long, Account> collectAccountsById() {
    method collectBalancesByEmailForAccountsCreatedOn (line 130) | public Map<String, BigDecimal> collectBalancesByEmailForAccountsCreate...
    method groupFirstNamesByLastNames (line 140) | public Map<String, Set<String>> groupFirstNamesByLastNames() {
    method groupCommaSeparatedFirstNamesByBirthdayMonth (line 150) | public Map<Month, String> groupCommaSeparatedFirstNamesByBirthdayMonth...
    method groupTotalBalanceByCreationMonth (line 160) | public Map<Month, BigDecimal> groupTotalBalanceByCreationMonth() {
    method getCharacterFrequencyInFirstNames (line 170) | public Map<Character, Long> getCharacterFrequencyInFirstNames() {
    method getCharacterFrequencyIgnoreCaseInFirstAndLastNames (line 181) | public Map<Character, Long> getCharacterFrequencyIgnoreCaseInFirstAndL...

FILE: 5-0-functional-programming/5-2-1-crazy-streams/src/main/java/com/bobocode/fp/exception/EntityNotFoundException.java
  class EntityNotFoundException (line 3) | public class EntityNotFoundException extends RuntimeException {
    method EntityNotFoundException (line 4) | public EntityNotFoundException(String message) {

FILE: 5-0-functional-programming/5-2-1-crazy-streams/src/test/java/com/bobocode/fp/CrazyStreamsTest.java
  class CrazyStreamsTest (line 28) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method setUp (line 43) | @BeforeEach
    method findRichestPerson (line 48) | @Test
    method findAccountsByBirthdayMonth (line 57) | @Test
    method separateMaleAccounts (line 66) | @Test
    method getExpectedMaleMap (line 75) | private Map<Boolean, List<Account>> getExpectedMaleMap() {
    method getExpectedList (line 82) | private List<Account> getExpectedList() {
    method groupAccountsByEmailDomain (line 86) | @Test
    method getExpectedEmailMap (line 95) | private Map<String, List<Account>> getExpectedEmailMap() {
    method getNumOfLettersInFirstAndLastNames (line 104) | @Test
    method calculateTotalBalance (line 112) | @Test
    method sortByFirstAndLastNames (line 121) | @Test
    method containsAccountWithEmailDomain (line 133) | @Test
    method getBalanceByEmail (line 141) | @Test
    method getBalanceByEmailThrowsException (line 150) | @Test
    method collectAccountsById (line 163) | @Test
    method collectBalancesByEmailForAccountsCreatedOn (line 174) | @Test
    method groupFirstNamesByLastNames (line 184) | @Test
    method groupCommaSeparatedFirstNamesByBirthdayMonth (line 196) | @Test
    method groupTotalBalanceByCreationMonth (line 207) | @Test
    method getCharacterFrequencyInFirstNames (line 217) | @Test
    method getCharacterFrequencyIgnoreCaseInFirstAndLastNames (line 231) | @MethodSource("getCharacterFrequencyIgnoreCaseInFirstAndLastNamesArgs")
    method getCharacterFrequencyIgnoreCaseInFirstAndLastNamesArgs (line 240) | private static Stream<Arguments> getCharacterFrequencyIgnoreCaseInFirs...
    method buildMap (line 248) | private static Map<Character, Long> buildMap(List<Account> accounts, i...
    method processName (line 257) | private static void processName(Map<Character, Long> resultMap, String...

FILE: 5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/CrazyOptionals.java
  class CrazyOptionals (line 31) | public class CrazyOptionals {
    method optionalOfString (line 39) | public static Optional<String> optionalOfString(@Nullable String text) {
    method deposit (line 49) | public static void deposit(AccountProvider accountProvider, BigDecimal...
    method optionalOfAccount (line 59) | public static Optional<Account> optionalOfAccount(@Nonnull Account acc...
    method getAccount (line 71) | public static Account getAccount(AccountProvider accountProvider, Acco...
    method processAccount (line 82) | public static void processAccount(AccountProvider accountProvider, Acc...
    method getOrGenerateAccount (line 93) | public static Account getOrGenerateAccount(AccountProvider accountProv...
    method retrieveBalance (line 103) | public static Optional<BigDecimal> retrieveBalance(AccountProvider acc...
    method getAccount (line 114) | public static Account getAccount(AccountProvider accountProvider) {
    method retrieveCreditBalance (line 124) | public static Optional<BigDecimal> retrieveCreditBalance(CreditAccount...
    method retrieveAccountGmail (line 136) | public static Optional<Account> retrieveAccountGmail(AccountProvider a...
    method getAccountWithFallback (line 149) | public static Account getAccountWithFallback(AccountProvider accountPr...
    method getAccountWithMaxBalance (line 160) | public static Account getAccountWithMaxBalance(List<Account> accounts) {
    method findMinBalanceValue (line 170) | public static OptionalDouble findMinBalanceValue(List<Account> account...
    method processAccountWithMaxBalance (line 180) | public static void processAccountWithMaxBalance(List<Account> accounts...
    method calculateTotalCreditBalance (line 190) | public static double calculateTotalCreditBalance(List<CreditAccount> a...

FILE: 5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/exception/AccountNotFoundException.java
  class AccountNotFoundException (line 3) | public class AccountNotFoundException extends RuntimeException {
    method AccountNotFoundException (line 4) | public AccountNotFoundException(String message) {

FILE: 5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/AccountProvider.java
  type AccountProvider (line 7) | @FunctionalInterface
    method getAccount (line 9) | Optional<Account> getAccount();

FILE: 5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/AccountService.java
  type AccountService (line 5) | @FunctionalInterface
    method processAccount (line 7) | void processAccount(Account account);
    method processWithNoAccount (line 9) | default void processWithNoAccount(){

FILE: 5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/CreditAccountProvider.java
  type CreditAccountProvider (line 7) | @FunctionalInterface
    method getAccount (line 9) | Optional<CreditAccount> getAccount();

FILE: 5-0-functional-programming/5-3-1-crazy-optionals/src/test/java/com/bobocode/fp/CrazyOptionalsTest.java
  class CrazyOptionalsTest (line 27) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method optionalOfStringShouldAcceptNull (line 30) | @Test
    method optionalOfStringShouldHoldSting (line 38) | @Test
    method depositWhenAccountExistsShouldUpdateBalance (line 46) | @Test
    method depositWhenOptionalIsEmptyShouldDoNothing (line 58) | @Test
    method optionalOfAccountShouldNotAcceptNull (line 64) | @Test
    method optionalOfAccountShouldHoldAccount (line 70) | @Test
    method getAccountShouldReturnAccountGotFromProvider (line 79) | @Test
    method getAccountWhenNoAccountIsProvidedShouldReturnDefaultAccount (line 90) | @Test
    method processAccountWhenAccountIsProvidedShouldPassAccountToService (line 100) | @Test
    method processAccountWhenNoAccountProvidedShouldProcessWithNoAccount (line 112) | @Test
    method processAccountShouldUseNullSafeDeclarativeIfStatement (line 125) | @Test
    method getOrGenerateAccountShouldReturnAccountGotFromProvider (line 136) | @Test
    method getOrGenerateAccountWhenNoAccountIsProvidedShouldGenerateAccount (line 146) | @Test
    method getOrGenerateAccountWhenNoAccountIsProvidedShouldUseLazyInitialization (line 154) | @Test
    method retrieveBalanceWhenAccountIsNotProvidedShouldReturnEmptyOptional (line 166) | @Test
    method retrieveBalanceWhenBalanceIsNullShouldReturnEmptyOptional (line 174) | @Test
    method retrieveBalanceShouldReturnOptionalBalance (line 185) | @Test
    method retrieveBalanceShouldUseNullSafeMapping (line 195) | @Test
    method getAccountShouldReturnProvidedAccount (line 209) | @Test
    method getAccountWhenNoAccountIsProvidedShouldThrowAccountNotFoundException (line 219) | @Test
    method retrieveCreditBalanceWhenAccountIsNotProvidedShouldReturnEmptyOptional (line 227) | @Test
    method retrieveCreditBalanceWhenBalanceIsNullShouldReturnEmptyOptional (line 235) | @Test
    method retrieveCreditBalanceShouldReturnOptionalBalance (line 246) | @Test
    method retrieveCreditBalanceShouldUseNullSafeMapping (line 256) | @Test
    method retrieveAccountGmailWhenNoAccountProvidedShouldReturnEmptyOptional (line 270) | @Test
    method retrieveAccountGmailWhenAccountEmailIsNotGmailShouldReturnEmptyOptional (line 278) | @Test
    method retrieveAccountGmailWhenEmailIsGmailShouldReturnEmail (line 289) | @Test
    method retrieveAccountGmailShouldUseNullSafeFiltering (line 300) | @Test
    method getAccountWithFallbackShouldBeRetrievedFromMainProvider (line 314) | @Test
    method getAccountWithFallbackWhenNoAccountIsProvidedByMainProviderShouldUseFallback (line 325) | @Test
    method getAccountWithFallbackWhenNoAccountShouldThrowException (line 335) | @Test
    method getAccountWithFallbackShouldUseNullSafeFallbackStrategy (line 342) | @Test
    method getAccountWithMaxBalance (line 357) | @Test
    method getAccountWithMaxBalanceWhenListIsEmptyShouldThrowException (line 368) | @Test
    method findMinBalanceValueShouldReturnCorrectDoubleValue (line 375) | @Test
    method findMinBalanceValueWhenListIsEmptyShouldReturnOptionalEmpty (line 387) | @Test
    method processAccountWithMaxBalance (line 395) | @Test
    method calculateTotalCreditBalanceShouldCalculateCorrectTotal (line 407) | @Test
    method calculateTotalCreditBalanceWhenListIsEmptyShouldReturnZero (line 418) | @Test
    method getMaxAccount (line 426) | private Account getMaxAccount(List<Account> accounts, Comparator<Accou...
    method calculateTotalCreditBalance (line 432) | private double calculateTotalCreditBalance(List<CreditAccount> account...

FILE: 5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/OOSumOfPrimes.java
  class OOSumOfPrimes (line 11) | public class OOSumOfPrimes {
    method main (line 12) | public static void main(String[] args) {
    method isPrime (line 26) | private static boolean isPrime(int n) {

FILE: 5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java
  class PrimeNumbers (line 22) | public class PrimeNumbers {
    method PrimeNumbers (line 23) | private PrimeNumbers() {
    method stream (line 32) | public static IntStream stream() {
    method stream (line 42) | public static IntStream stream(int size) {
    method sum (line 53) | public static int sum(int n) {
    method list (line 63) | public static List<Integer> list(int n) {
    method processByIndex (line 73) | public static void processByIndex(int idx, IntConsumer consumer) {
    method groupByAmountOfDigits (line 87) | public static Map<Integer, List<Integer>> groupByAmountOfDigits(int n) {

FILE: 5-0-functional-programming/5-4-1-fun-prime-numbers/src/test/java/com/bobocode/fp/PrimeNumbersTest.java
  class PrimeNumbersTest (line 26) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    method stream (line 29) | @Order(1)
    method streamN (line 42) | @Order(2)
    method sum (line 54) | @Order(3)
    method collect (line 63) | @Order(4)
    method processByIndexFindsCorrectPrimeNumber (line 72) | @Order(5)
    method collectArgs (line 83) | static Stream<Arguments> collectArgs() {
    method groupByAmountOfDigits (line 94) | @Order(6)
    method groupByAmountOfDigitsArgs (line 103) | static Stream<Arguments> groupByAmountOfDigitsArgs() {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/CrazyLambdaExample.java
  class CrazyLambdaExample (line 11) | public class CrazyLambdaExample {
    method main (line 12) | public static void main(String[] args) {
    method getStringPredicateToConsumerFunctionSupplier (line 27) | private static Supplier<Function<Predicate<String>, Consumer<String>>>...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/FunctionComposition.java
  class FunctionComposition (line 16) | public class FunctionComposition {
    method main (line 17) | public static void main(String[] args) {
    method printSquareOfDoubleUsingFunctionComposition (line 22) | private static void printSquareOfDoubleUsingFunctionComposition() {
    method printStringIsBlankUsingPredicateComposition (line 31) | private static void printStringIsBlankUsingPredicateComposition() {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaAndMethodReference.java
  class LambdaAndMethodReference (line 12) | public class LambdaAndMethodReference {
    method main (line 13) | public static void main(String[] args) {
    method printAccountsUsingLambda (line 20) | private static void printAccountsUsingLambda(List<Account> accounts) {
    method printAccountsUsingMethodReference (line 24) | private static void printAccountsUsingMethodReference(List<Account> ac...
    method processAccounts (line 28) | private static void processAccounts(List<Account> accounts, Consumer<A...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaComparatorExample.java
  class LambdaComparatorExample (line 11) | public class LambdaComparatorExample {
    method main (line 13) | public static void main(String[] args) {
    method sortUsingAnonymousClass (line 20) | private static void sortUsingAnonymousClass(List<Account> accounts) {
    method sortUsingLambda (line 29) | private static void sortUsingLambda(List<Account> accounts) {
    method sortUsingMethodReference (line 33) | private static void sortUsingMethodReference(List<Account> accounts) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaRunnableExample.java
  class LambdaRunnableExample (line 3) | public class LambdaRunnableExample {
    method main (line 4) | public static void main(String[] args) {
    method sayHelloFromNewThread (line 10) | private static void sayHelloFromNewThread() {
    method sayHelloFromNewThreadUsingLambda (line 21) | private static void sayHelloFromNewThreadUsingLambda() {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/MethodRefToUpperCaseExample.java
  class MethodRefToUpperCaseExample (line 10) | public class MethodRefToUpperCaseExample {
    method main (line 11) | public static void main(String[] args) {
    method printUpperStringUsingFunction (line 23) | private static void printUpperStringUsingFunction(String s) {
    method printUpperStringUsingUnaryOperation (line 33) | private static void printUpperStringUsingUnaryOperation(String s) {
    method printUpperStringUsingStringSupplier (line 44) | private static void printUpperStringUsingStringSupplier(String s) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/MethodReferenceExamples.java
  class MethodReferenceExamples (line 10) | public class MethodReferenceExamples {
    method main (line 11) | public static void main(String[] args) {
    method printAbsUsingMethodReference (line 17) | private static void printAbsUsingMethodReference(int a) {
    method printSumUsingMethodReference (line 24) | private static void printSumUsingMethodReference(int a, int b) {
    method printUpperStringUsingMethodReference (line 31) | private static void printUpperStringUsingMethodReference(String s) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/PredefinedInterfacePrimitives.java
  class PredefinedInterfacePrimitives (line 11) | public class PredefinedInterfacePrimitives {
    method main (line 12) | public static void main(String[] args) {
    method printRandomDoubleUsingSupplier (line 18) | private static void printRandomDoubleUsingSupplier() {
    method printLongSumUsingBinaryOperation (line 24) | private static void printLongSumUsingBinaryOperation(long a, long b) {
    method printSqrtUsingFunction (line 30) | private static void printSqrtUsingFunction(int a) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/PredefinedInterfacesExamples.java
  class PredefinedInterfacesExamples (line 13) | public class PredefinedInterfacesExamples {
    method main (line 14) | public static void main(String[] args) {
    method printARandomNumberUsingIntegerSupplier (line 23) | private static void printARandomNumberUsingIntegerSupplier() {
    method printEmailUsingAccountConsumer (line 29) | private static void printEmailUsingAccountConsumer() {
    method calculate3xValueUsingIntegerFunction (line 36) | private static void calculate3xValueUsingIntegerFunction() {
    method checkIfNumberIsPositiveUsingIntegerPredicate (line 43) | private static void checkIfNumberIsPositiveUsingIntegerPredicate() {
    method verifyGoogleEmailUsingAccountPredicate (line 50) | private static void verifyGoogleEmailUsingAccountPredicate() {
    method printPrimeNumbersUsingIntegerPredicate (line 58) | private static void printPrimeNumbersUsingIntegerPredicate() {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalCreation.java
  class OptionalCreation (line 8) | public class OptionalCreation {
    method main (line 9) | public static void main(String[] args) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalExamples.java
  class OptionalExamples (line 10) | public class  OptionalExamples {
    method main (line 13) | public static void main(String[] args) {
    method printAccountUsingWrongApproach (line 27) | private static void printAccountUsingWrongApproach(Optional<Account> o...
    method printAccountUsingRightApproach (line 37) | private static void printAccountUsingRightApproach(Optional<Account> o...
    method printAccountOrDefault (line 45) | private static void printAccountOrDefault(Optional<Account> optionalAc...
    method printAccountOrRandomLazily (line 54) | private static void printAccountOrRandomLazily(Optional<Account> optio...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalImperativeVsDeclarativeCheck.java
  class OptionalImperativeVsDeclarativeCheck (line 8) | public class OptionalImperativeVsDeclarativeCheck {
    method main (line 9) | public static void main(String[] args) {
    method printAccount (line 20) | private static void printAccount(Account account) {
    method printOptionalAccount (line 32) | private static void printOptionalAccount(Optional<Account> optionalAcc...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalMapping.java
  class OptionalMapping (line 13) | public class OptionalMapping {
    method main (line 14) | public static void main(String[] args) {
    method printBalanceUsingNullCheck (line 30) | private static void printBalanceUsingNullCheck(Account account) {
    method printBalanceUsingOptionalMapping (line 42) | private static void printBalanceUsingOptionalMapping(Optional<Account>...
    method printBalanceOfFirstGoogleAccount (line 49) | private static void printBalanceOfFirstGoogleAccount(List<Account> acc...
    method printCreditBalanceOfFirstGoogleAccount (line 61) | private static void printCreditBalanceOfFirstGoogleAccount(List<Credit...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalPrimitives.java
  class OptionalPrimitives (line 10) | public class OptionalPrimitives {
    method main (line 11) | public static void main(String[] args) {
    method findMinBalance (line 23) | private static OptionalDouble findMinBalance(List<Account> accounts) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalReturningMethod.java
  class OptionalReturningMethod (line 10) | public class OptionalReturningMethod {
    method main (line 11) | public static void main(String[] args) {
    method findLuckyGuy (line 22) | private static Optional<Account> findLuckyGuy(List<Account> accounts) {
    method printResult (line 35) | private static void printResult(Optional<Account> optionalAccount) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalSearchByEmailExample.java
  class OptionalSearchByEmailExample (line 9) | public class OptionalSearchByEmailExample {
    method main (line 10) | public static void main(String[] args) {
    method findAccountByEmail (line 18) | private static Optional<Account> findAccountByEmail(List<Account> acco...
    method getAccountByEmail (line 24) | private static Account getAccountByEmail(List<Account> accounts, Strin...
    method getAccountByEmailImperatively (line 32) | private static Account getAccountByEmailImperatively(List<Account> acc...
    method printAccount (line 42) | private static void printAccount(Account account) {
    class NoSuchElementException (line 47) | static class NoSuchElementException extends RuntimeException {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalStream.java
  class OptionalStream (line 9) | public class OptionalStream {
    method main (line 10) | public static void main(String[] args) {
    method printExistingCreditBalancesJava8 (line 17) | private static void printExistingCreditBalancesJava8(List<CreditAccoun...
    method printExistingCreditBalancesJava9 (line 25) | private static void printExistingCreditBalancesJava9(List<CreditAccoun...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/ImperativeVsDeclarativeFiltering.java
  class ImperativeVsDeclarativeFiltering (line 11) | public class ImperativeVsDeclarativeFiltering {
    method main (line 12) | public static void main(String[] args) {
    method findAllGmailAccountsImperatively (line 20) | private static List<Account> findAllGmailAccountsImperatively(List<Acc...
    method findAllGmailAccountsDeclaratively (line 30) | private static List<Account> findAllGmailAccountsDeclaratively(List<Ac...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/ImperativeVsDeclarativeMax.java
  class ImperativeVsDeclarativeMax (line 10) | public class ImperativeVsDeclarativeMax {
    method main (line 11) | public static void main(String[] args) {
    method printAccountWithMaxBalanceDeclaratively (line 18) | private static void printAccountWithMaxBalanceDeclaratively(List<Accou...
    method printAccountWithMaxBalanceImperatively (line 26) | private static void printAccountWithMaxBalanceImperatively(List<Accoun...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamAdditionalFeatures.java
  class StreamAdditionalFeatures (line 16) | public class StreamAdditionalFeatures {
    method main (line 17) | public static void main(String[] args) {
    method printSortedFirstNames (line 26) | private static void printSortedFirstNames(List<Account> accounts) {
    method printThirdAndFourthFirstNames (line 37) | private static void printThirdAndFourthFirstNames(List<Account> accoun...
    method printBalanceStatistic (line 47) | private static void printBalanceStatistic(List<Account> accounts) {
    method printFirstNamesAndCollectEmails (line 59) | private static void printFirstNamesAndCollectEmails(List<Account> acco...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamBasics.java
  class StreamBasics (line 22) | public class StreamBasics {
    method main (line 23) | public static void main(String[] args) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamCollecting.java
  class StreamCollecting (line 22) | public class StreamCollecting {
    method main (line 23) | public static void main(String[] args) {
    method printAllGmailAccounts (line 34) | private static void printAllGmailAccounts(List<Account> accounts) {
    method printAccountsByItsBirthdaysMonth (line 44) | private static void printAccountsByItsBirthdaysMonth(List<Account> acc...
    method printAccountFirstNamesByItsBirthdaysMonth (line 51) | private static void printAccountFirstNamesByItsBirthdaysMonth(List<Acc...
    method printFirstNamesSeparatingGmailAccountsAndOthers (line 58) | private static void printFirstNamesSeparatingGmailAccountsAndOthers(Li...
    method printCommaSeparatedNames (line 69) | private static void printCommaSeparatedNames(List<Account> accounts) {
    method printNamesSeparatedGmailAccountAndOthersAndGroupedByBirthdayMonth (line 82) | private static void printNamesSeparatedGmailAccountAndOthersAndGrouped...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamFiltering.java
  class StreamFiltering (line 10) | public class StreamFiltering {
    method main (line 11) | public static void main(String[] args) {
    method printAccountsThatHaveGoogleEmail (line 21) | private static void printAccountsThatHaveGoogleEmail(List<Account> acc...

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamMapping.java
  class StreamMapping (line 15) | public class StreamMapping {
    method main (line 16) | public static void main(String[] args) {
    method printAccountEmails (line 28) | private static void printAccountEmails(List<Account> accounts) {
    method printCharacterCounts (line 35) | private static void printCharacterCounts() {
    method collectCharactersCountFromText (line 46) | private static Map<Character, Long> collectCharactersCountFromText(Str...
    method getSomeText (line 54) | private static String getSomeText() {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamParallelProcessing.java
  class StreamParallelProcessing (line 7) | public class StreamParallelProcessing {
    method main (line 11) | public static void main(String[] args) {
    method performNTimes (line 27) | static void performNTimes(int n, Runnable r) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamPrimitives.java
  class StreamPrimitives (line 11) | public class StreamPrimitives {
    method main (line 12) | public static void main(String[] args) {
    method printLongValues (line 24) | private static void printLongValues() {
    method printMaxBalance (line 35) | private static void printMaxBalance(List<Account> accounts) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamReducing.java
  class StreamReducing (line 14) | public class StreamReducing {
    method main (line 15) | public static void main(String[] args) {
    method printTotalBalance (line 22) | private static void printTotalBalance(List<Account> accounts) {
    method printMaxBalance (line 34) | private static void printMaxBalance(List<Account> accounts) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamSideEffectFilteringExample.java
  class StreamSideEffectFilteringExample (line 9) | public class StreamSideEffectFilteringExample {
    method main (line 10) | public static void main(String[] args) {

FILE: 5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamWhileExample.java
  class StreamWhileExample (line 8) | public class StreamWhileExample {
    method main (line 9) | public static void main(String[] args) {
    method printAllEmails (line 17) | private static void printAllEmails(List<Account> accounts) {
    method printAllEmailsWhileGmail (line 24) | private static void printAllEmailsWhileGmail(List<Account> accounts) {
    method printAllEmailsWhileNotGmail (line 32) | private static void printAllEmailsWhileNotGmail(List<Account> accounts) {

FILE: 6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/LinkedStack.java
  class LinkedStack (line 5) | public class LinkedStack<T> implements Stack<T> {
    method push (line 6) | @Override
    method pop (line 11) | @Override
    method size (line 16) | @Override
    method isEmpty (line 21) | @Override

FILE: 6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/Stack.java
  type Stack (line 7) | public interface Stack<T> {
    method push (line 13) | void push(T element);
    method pop (line 20) | T pop();
    method size (line 27) | int size();
    method isEmpty (line 34) | boolean isEmpty();

FILE: 6-0-test-driven-development/6-1-1-stack/src/test/java/com/bobocode/tdd/StackTest.java
  class StackTest (line 3) | public class StackTest {

FILE: 6-0-test-driven-development/6-1-2-linked-list/src/main/java/com/bobocode/tdd/LinkedList.java
  class LinkedList (line 12) | public class LinkedList<T> implements List<T> {
    method of (line 21) | public static <T> List<T> of(T... elements) {
    method add (line 30) | @Override
    method add (line 42) | @Override
    method set (line 54) | @Override
    method get (line 66) | @Override
    method getFirst (line 77) | @Override
    method getLast (line 88) | @Override
    method remove (line 100) | @Override
    method contains (line 111) | @Override
    method isEmpty (line 121) | @Override
    method size (line 131) | @Override
    method clear (line 139) | @Override

FILE: 6-0-test-driven-development/6-1-2-linked-list/src/main/java/com/bobocode/tdd/List.java
  type List (line 4) | public interface List<T> {
    method add (line 5) | void add(T element);
    method add (line 7) | void add(int index, T element);
    method set (line 9) | void set(int index, T element);
    method get (line 11) | T get(int index);
    method getFirst (line 13) | T getFirst();
    method getLast (line 15) | T getLast();
    method remove (line 17) | T remove(int index);
    method contains (line 19) | boolean contains(T element);
    method isEmpty (line 21) | boolean isEmpty();
    method size (line 23) | int size();
    method clear (line 25) | void clear();

FILE: 6-0-test-driven-development/6-1-2-linked-list/src/test/java/com/bobocode/tdd/LinkedListTest.java
  class LinkedListTest (line 3) | public class LinkedListTest {

FILE: 6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/BinarySearchTree.java
  type BinarySearchTree (line 5) | public interface BinarySearchTree<T extends Comparable> {
    method insert (line 10) | boolean insert(T element);
    method contains (line 15) | boolean contains(T element);
    method size (line 20) | int size();
    method depth (line 25) | int depth();
    method inOrderTraversal (line 31) | void inOrderTraversal(Consumer<T> consumer);

FILE: 6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java
  class RecursiveBinarySearchTree (line 7) | public class RecursiveBinarySearchTree<T extends Comparable> implements ...
    method of (line 9) | public static <T extends Comparable> RecursiveBinarySearchTree<T> of(T...
    method insert (line 13) | @Override
    method contains (line 18) | @Override
    method size (line 23) | @Override
    method depth (line 28) | @Override
    method inOrderTraversal (line 33) | @Override

FILE: 6-0-test-driven-development/6-1-3-binary-search-tree/src/test/java/com/bobocode/tdd/BinarySearchTreeTest.java
  class BinarySearchTreeTest (line 3) | class BinarySearchTreeTest {

FILE: java-fundamentals-util/src/main/java/com/bobocode/data/Accounts.java
  class Accounts (line 17) | public class Accounts {
    method generateAccount (line 20) | public static Account generateAccount() {
    method generateCreditAccount (line 27) | public static CreditAccount generateCreditAccount() {
    method generateAccountList (line 35) | public static List<Account> generateAccountList(int size) {
    method generateCreditAccountList (line 41) | public static List<CreditAccount> generateCreditAccountList(int size) {
    method generatePerson (line 47) | private static Person generatePerson() {
    method convertToAccount (line 52) | private static Account convertToAccount(Person person) {
    method convertToCreditAccount (line 58) | private static CreditAccount convertToCreditAccount(Person person) {
    method fillAccount (line 64) | private static void fillAccount(Account account, Person person) {
    method fillCommonRandomFields (line 75) | private static void fillCommonRandomFields(Account account) {
    method randomBigDecimal (line 81) | private static BigDecimal randomBigDecimal(int max) {

FILE: java-fundamentals-util/src/main/java/com/bobocode/model/Account.java
  class Account (line 8) | @NoArgsConstructor

FILE: java-fundamentals-util/src/main/java/com/bobocode/model/CreditAccount.java
  class CreditAccount (line 11) | @NoArgsConstructor
    method getCreditBalance (line 17) | public Optional<BigDecimal> getCreditBalance() {

FILE: java-fundamentals-util/src/main/java/com/bobocode/model/Sex.java
  type Sex (line 3) | public enum Sex {

FILE: java-fundamentals-util/src/main/java/com/bobocode/util/ExerciseNotCompletedException.java
  class ExerciseNotCompletedException (line 7) | public class ExerciseNotCompletedException extends RuntimeException {
    method ExerciseNotCompletedException (line 8) | public ExerciseNotCompletedException() {

FILE: lesson-demo/src/main/java/com/bobocode/DemoApp.java
  class DemoApp (line 3) | public class DemoApp {
    method main (line 4) | public static void main(String[] args) {
Condensed preview — 201 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (554K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 65,
    "preview": "# These are supported funding model platforms\n\npatreon: bobocode\n"
  },
  {
    "path": ".github/workflows/maven.yml",
    "chars": 561,
    "preview": "# This workflow will build a Java project with Maven\n# For more information see: https://help.github.com/actions/languag"
  },
  {
    "path": ".github/workflows/projectActions.yml",
    "chars": 596,
    "preview": "name: Auto Assign to Project(s)\n\non:\n  issues:\n    types: [opened, labeled]\n  pull_request:\n    types: [opened, labeled]"
  },
  {
    "path": ".gitignore",
    "chars": 25,
    "preview": ".idea\n**/*.iml\n**/target\n"
  },
  {
    "path": ".mvn/wrapper/MavenWrapperDownloader.java",
    "chars": 4941,
    "preview": "/*\n * Copyright 2007-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \""
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "chars": 218,
    "preview": "distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip\nwrap"
  },
  {
    "path": "0-0-intro/README.md",
    "chars": 1723,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "0-0-intro/pom.xml",
    "chars": 751,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "0-0-intro/src/main/java/com/bobocode/intro/ExerciseIntroduction.java",
    "chars": 1791,
    "preview": "package com.bobocode.intro;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * Welcome! This is an introdu"
  },
  {
    "path": "0-0-intro/src/test/java/com/bobocode/intro/ExerciseIntroductionTest.java",
    "chars": 1932,
    "preview": "package com.bobocode.intro;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.*;\n\nimport java.util.Arrays;\n\nimpo"
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/README.MD",
    "chars": 1482,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/pom.xml",
    "chars": 519,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/Box.java",
    "chars": 704,
    "preview": "package com.bobocode.basics;\n\n/**\n * {@link Box} is a container class that can store a value of any given type. Using Ob"
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/src/main/java/com/bobocode/basics/BoxDemoApp.java",
    "chars": 919,
    "preview": "package com.bobocode.basics;\n\n/**\n * This demo demonstrates why using Object is not safe. It's not safe because runtime "
  },
  {
    "path": "1-0-java-basics/1-3-0-hello-generics/src/test/java/com/bobocode/basics/BoxTest.java",
    "chars": 2593,
    "preview": "package com.bobocode.basics;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.*;\n\nimport static org.assertj.cor"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/README.MD",
    "chars": 1958,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/pom.xml",
    "chars": 523,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/CrazyGenerics.java",
    "chars": 9959,
    "preview": "package com.bobocode.basics;\n\nimport com.bobocode.basics.util.BaseEntity;\nimport com.bobocode.util.ExerciseNotCompletedE"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/src/main/java/com/bobocode/basics/util/BaseEntity.java",
    "chars": 443,
    "preview": "package com.bobocode.basics.util;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstruct"
  },
  {
    "path": "1-0-java-basics/1-3-1-crazy-generics/src/test/java/com/bobocode/basics/CrazyGenericsTest.java",
    "chars": 32929,
    "preview": "package com.bobocode.basics;\n\nimport com.bobocode.basics.CrazyGenerics.*;\nimport com.bobocode.basics.util.BaseEntity;\nim"
  },
  {
    "path": "1-0-java-basics/1-3-2-heterogeneous-max-holder/README.MD",
    "chars": 1073,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "1-0-java-basics/1-3-2-heterogeneous-max-holder/pom.xml",
    "chars": 533,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "1-0-java-basics/1-3-2-heterogeneous-max-holder/src/main/java/com/bobocode/basics/HeterogeneousMaxHolder.java",
    "chars": 2389,
    "preview": "package com.bobocode.basics;\n\nimport java.util.Map;\n\n/**\n * {@link HeterogeneousMaxHolder} is a multi-type container tha"
  },
  {
    "path": "1-0-java-basics/1-3-2-heterogeneous-max-holder/src/test/java/com/bobocode/basics/HeterogeneousMaxHolderTest.java",
    "chars": 16574,
    "preview": "package com.bobocode.basics;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\nimport lombok.Sneaky"
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/README.MD",
    "chars": 1172,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/pom.xml",
    "chars": 522,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/HelloAnnotationsExercise.java",
    "chars": 818,
    "preview": "package com.bobocode.basics;\n\n/**\n * {@link HelloAnnotationsExercise} is an exercise class that is marked with be corres"
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/src/main/java/com/bobocode/basics/Level.java",
    "chars": 157,
    "preview": "package com.bobocode.basics;\n\n/**\n * Enum that lists all possible exercise complexity levels.\n */\npublic enum Level {\n  "
  },
  {
    "path": "1-0-java-basics/1-5-0-hello-annotations/src/test/java/com/bobocode/basics/HelloAnnotationsExerciseTest.java",
    "chars": 3270,
    "preview": "package com.bobocode.basics;\n\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.*;\n\nimport java.lang.annotation."
  },
  {
    "path": "1-0-java-basics/README.md",
    "chars": 229,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "1-0-java-basics/pom.xml",
    "chars": 1049,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/README.MD",
    "chars": 1011,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/pom.xml",
    "chars": 529,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/src/main/java/com/bobobode/cs/Node.java",
    "chars": 453,
    "preview": "package com.bobobode.cs;\n\n/**\n * Class {@link Node} is a very simple data structure that consists of an element itself a"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/src/main/java/com/bobobode/cs/Nodes.java",
    "chars": 3419,
    "preview": "package com.bobobode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * A class that consists of stati"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-1-node/src/test/java/com/bobocode/cs/NodesTest.java",
    "chars": 5744,
    "preview": "package com.bobocode.cs;\n\nimport com.bobobode.cs.Node;\nimport com.bobobode.cs.Nodes;\nimport lombok.SneakyThrows;\nimport "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/README.md",
    "chars": 1720,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/pom.xml",
    "chars": 530,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/LinkedStack.java",
    "chars": 2168,
    "preview": "package com.bobocode.cs;\n\nimport com.bobocode.cs.exception.EmptyStackException;\nimport com.bobocode.util.ExerciseNotComp"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/Stack.java",
    "chars": 481,
    "preview": "package com.bobocode.cs;\n\n/**\n * {@link Stack} is a fundamental data structure that follows last-in-first-out (LIFO) pri"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-2-stack/src/main/java/com/bobocode/cs/exception/EmptyStackException.java",
    "chars": 98,
    "preview": "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",
    "chars": 12239,
    "preview": "package com.bobocode.cs;\n\nimport com.bobocode.cs.exception.EmptyStackException;\nimport lombok.SneakyThrows;\nimport org.j"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/README.MD",
    "chars": 887,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/pom.xml",
    "chars": 537,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/main/java/com/bobocode/cs/LinkedQueue.java",
    "chars": 1667,
    "preview": "package com.bobocode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link LinkedQueue} implements"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/main/java/com/bobocode/cs/Queue.java",
    "chars": 844,
    "preview": "package com.bobocode.cs;\n\n/**\n * Queue is a data structure that follows \"first in, first out\" rule (FIFO). Operations {@"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-3-linked-queue/src/test/java/com/bobocode/cs/LinkedQueueTest.java",
    "chars": 11855,
    "preview": "package com.bobocode.cs;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupit"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-4-linked-list/README.MD",
    "chars": 1216,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-4-linked-list/pom.xml",
    "chars": 784,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-4-linked-list/src/main/java/com/bobocode/cs/LinkedList.java",
    "chars": 4400,
    "preview": "package com.bobocode.cs;\n\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link LinkedList} is a list "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-4-linked-list/src/test/java/com/bobocode/cs/LinkedListTest.java",
    "chars": 16735,
    "preview": "package com.bobocode.cs;\n\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupi"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-5-array-list/README.MD",
    "chars": 1117,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-5-array-list/pom.xml",
    "chars": 778,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-5-array-list/src/main/java/com/bobocode/cs/ArrayList.java",
    "chars": 4846,
    "preview": "package com.bobocode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link ArrayList} is an implem"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-5-array-list/src/test/java/com/bobocode/cs/ArrayListTest.java",
    "chars": 10876,
    "preview": "package com.bobocode.cs;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupit"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/README.md",
    "chars": 1222,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/pom.xml",
    "chars": 543,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/main/java/com/bobocode/cs/BinarySearchTree.java",
    "chars": 795,
    "preview": "package com.bobocode.cs;\n\nimport java.util.function.Consumer;\n\npublic interface BinarySearchTree<T extends Comparable<T>"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/main/java/com/bobocode/cs/RecursiveBinarySearchTree.java",
    "chars": 1461,
    "preview": "package com.bobocode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.function.Consumer;\n\n"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-6-binary-search-tree/src/test/java/com/bobocode/cs/RecursiveBinarySearchTreeTest.java",
    "chars": 13166,
    "preview": "package com.bobocode.cs;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupit"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/README.md",
    "chars": 1021,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/pom.xml",
    "chars": 535,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/HashTable.java",
    "chars": 6403,
    "preview": "package com.bobocode.cs;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link HashTable} is a simple "
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/src/main/java/com/bobocode/cs/Map.java",
    "chars": 1912,
    "preview": "package com.bobocode.cs;\n\n/**\n * A {@link Map} is a simplified interface of so-called dictionary. It maps keys to values"
  },
  {
    "path": "2-0-data-structures-and-algorithms/2-2-9-hash-table/src/test/java/com/bobocode/cs/HashTableTest.java",
    "chars": 21963,
    "preview": "package com.bobocode.cs;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.ClassOrderer.OrderAnnotation;\nimport "
  },
  {
    "path": "2-0-data-structures-and-algorithms/README.md",
    "chars": 268,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "2-0-data-structures-and-algorithms/data-structures-and-algorithms-util/pom.xml",
    "chars": 554,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "2-0-data-structures-and-algorithms/data-structures-and-algorithms-util/src/main/java/com/bobocode/cs/List.java",
    "chars": 335,
    "preview": "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   "
  },
  {
    "path": "2-0-data-structures-and-algorithms/pom.xml",
    "chars": 1227,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/README.MD",
    "chars": 855,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/pom.xml",
    "chars": 515,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/main/java/com/bobocode/se/FileReaders.java",
    "chars": 558,
    "preview": "package com.bobocode.se;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link FileReaders} provides a"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/test/java/com/bobocode/se/FileReadersTest.java",
    "chars": 800,
    "preview": "package com.bobocode.se;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEqual"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/test/resources/empty.txt",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/test/resources/lines.txt",
    "chars": 21,
    "preview": "Hey!\n\nWhat's up?\n\nHi!"
  },
  {
    "path": "3-0-java-core/3-6-1-file-reader/src/test/resources/simple.txt",
    "chars": 24,
    "preview": "Hello!\nIt's a test file."
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/README.MD",
    "chars": 913,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/pom.xml",
    "chars": 514,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/main/java/com/bobocode/se/FileStats.java",
    "chars": 1589,
    "preview": "package com.bobocode.se;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link FileStats} provides an "
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/main/java/com/bobocode/se/FileStatsException.java",
    "chars": 267,
    "preview": "package com.bobocode.se;\n\npublic class FileStatsException extends RuntimeException{\n    public FileStatsException(String"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/test/java/com/bobocode/se/FileStatsTest.java",
    "chars": 2662,
    "preview": "package com.bobocode.se;\n\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.jun"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/test/resources/scosb.txt",
    "chars": 547,
    "preview": "We’re pleased to announce that the 2.0.1 release of Spring Cloud Open Service Broker is now available. This release reso"
  },
  {
    "path": "3-0-java-core/3-6-2-file-stats/src/test/resources/sotl.txt",
    "chars": 35562,
    "preview": "State of the Lambda\nSeptember 2013\nJava SE 8 Edition\nThis is an informal overview of the enhancements to the Java progra"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/README.MD",
    "chars": 2476,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/pom.xml",
    "chars": 515,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/src/main/java/com/bobocode/se/CrazyRegex.java",
    "chars": 7630,
    "preview": "package com.bobocode.se;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.regex.Pattern;\n\n/**\n"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/src/test/java/com/bobocode/se/CrazyRegexTest.java",
    "chars": 8868,
    "preview": "package com.bobocode.se;\n\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupit"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/src/test/resources/nasa.json",
    "chars": 1290,
    "preview": "{\"photos\":[\n  {\"id\":102693,\n    \"sol\":1000,\n    \"camera\":{\"id\":20,\"name\":\"FHAZ\",\"rover_id\":5,\"full_name\":\"Front Hazard A"
  },
  {
    "path": "3-0-java-core/3-6-3-crazy-regex/src/test/resources/note.txt",
    "chars": 647,
    "preview": "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"
  },
  {
    "path": "3-0-java-core/3-6-4-random-field-comparator/README.MD",
    "chars": 1068,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "3-0-java-core/3-6-4-random-field-comparator/pom.xml",
    "chars": 527,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "3-0-java-core/3-6-4-random-field-comparator/src/main/java/com/bobocode/se/RandomFieldComparator.java",
    "chars": 2163,
    "preview": "package com.bobocode.se;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\nimport java.util.Comparator;\n\n/**\n * A"
  },
  {
    "path": "3-0-java-core/3-6-4-random-field-comparator/src/test/java/com/bobocode/se/RandomFieldComparatorTest.java",
    "chars": 9455,
    "preview": "package com.bobocode.se;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\nimport static org.junit"
  },
  {
    "path": "3-0-java-core/README.md",
    "chars": 204,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "3-0-java-core/pom.xml",
    "chars": 1033,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/README.MD",
    "chars": 1359,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/pom.xml",
    "chars": 535,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/data/FlightDao.java",
    "chars": 1163,
    "preview": "package com.bobocode.oop.data;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.HashSet;\nimpor"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/factory/FlightServiceFactory.java",
    "chars": 570,
    "preview": "package com.bobocode.oop.factory;\n\nimport com.bobocode.oop.service.FlightService;\nimport com.bobocode.util.ExerciseNotCo"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/src/main/java/com/bobocode/oop/service/FlightService.java",
    "chars": 1061,
    "preview": "package com.bobocode.oop.service;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.List;\n\n/**\n"
  },
  {
    "path": "4-0-object-oriented-programming/4-3-1-flight-search/src/test/java/com/bobocode/oop/FlightServiceTest.java",
    "chars": 1897,
    "preview": "package com.bobocode.oop;\n\nimport com.bobocode.oop.factory.FlightServiceFactory;\nimport com.bobocode.oop.service.FlightS"
  },
  {
    "path": "4-0-object-oriented-programming/README.md",
    "chars": 252,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "4-0-object-oriented-programming/pom.xml",
    "chars": 914,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/README.MD",
    "chars": 868,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/pom.xml",
    "chars": 537,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/FunctionMap.java",
    "chars": 944,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.fp.exception.InvalidFunctionNameException;\n\nimport java.util.HashMap;\nimpo"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/Functions.java",
    "chars": 1083,
    "preview": "package com.bobocode.fp;\n\n/**\n * An util class that provides a factory method for creating an instance of a {@link Funct"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/src/main/java/com/bobocode/fp/exception/InvalidFunctionNameException.java",
    "chars": 238,
    "preview": "package com.bobocode.fp.exception;\n\npublic class InvalidFunctionNameException extends RuntimeException {\n    public Inva"
  },
  {
    "path": "5-0-functional-programming/5-0-1-lambda-functions-map/src/test/java/com/bobocode/fp/FunctionsTest.java",
    "chars": 2158,
    "preview": "package com.bobocode.fp;\n\nimport org.junit.jupiter.api.*;\n\nimport java.util.function.Function;\n\nimport static org.assert"
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/README.MD",
    "chars": 814,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/pom.xml",
    "chars": 538,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/SumOfSquares.java",
    "chars": 1250,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.fp.exception.InvalidRangeException;\n\n/**\n * This class allow to calculate "
  },
  {
    "path": "5-0-functional-programming/5-0-2-stream-sum-of-squares/src/main/java/com/bobocode/fp/exception/InvalidRangeException.java",
    "chars": 100,
    "preview": "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",
    "chars": 1630,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.fp.exception.InvalidRangeException;\nimport org.junit.jupiter.api.MethodOrd"
  },
  {
    "path": "5-0-functional-programming/5-1-1-crazy-lambdas/README.md",
    "chars": 3433,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/5-1-1-crazy-lambdas/pom.xml",
    "chars": 530,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "5-0-functional-programming/5-1-1-crazy-lambdas/src/main/java/com/bobocode/fp/CrazyLambdas.java",
    "chars": 9096,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.math.BigDecimal;\nimport j"
  },
  {
    "path": "5-0-functional-programming/5-1-1-crazy-lambdas/src/test/java/com/bobocode/fp/CrazyLambdasTest.java",
    "chars": 10112,
    "preview": "package com.bobocode.fp;\n\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.jun"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/README.MD",
    "chars": 1010,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/pom.xml",
    "chars": 530,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/src/main/java/com/bobocode/fp/CrazyStreams.java",
    "chars": 6990,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.model.Account;\nimport com.bobocode.util.ExerciseNotCompletedException;\nimp"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/src/main/java/com/bobocode/fp/exception/EntityNotFoundException.java",
    "chars": 185,
    "preview": "package com.bobocode.fp.exception;\n\npublic class EntityNotFoundException extends RuntimeException {\n    public EntityNot"
  },
  {
    "path": "5-0-functional-programming/5-2-1-crazy-streams/src/test/java/com/bobocode/fp/CrazyStreamsTest.java",
    "chars": 10336,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.fp.exception.EntityNotFoundException;\nimport com.bobocode.model.Account;\ni"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/README.md",
    "chars": 1258,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/pom.xml",
    "chars": 532,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/CrazyOptionals.java",
    "chars": 6832,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.fp.exception.AccountNotFoundException;\n"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/exception/AccountNotFoundException.java",
    "chars": 187,
    "preview": "package com.bobocode.fp.exception;\n\npublic class AccountNotFoundException extends RuntimeException {\n    public AccountN"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/AccountProvider.java",
    "chars": 193,
    "preview": "package com.bobocode.fp.function;\n\nimport com.bobocode.model.Account;\n\nimport java.util.Optional;\n\n@FunctionalInterface\n"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/AccountService.java",
    "chars": 245,
    "preview": "package com.bobocode.fp.function;\n\nimport com.bobocode.model.Account;\n\n@FunctionalInterface\npublic interface AccountServ"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/main/java/com/bobocode/fp/function/CreditAccountProvider.java",
    "chars": 211,
    "preview": "package com.bobocode.fp.function;\n\nimport com.bobocode.model.CreditAccount;\n\nimport java.util.Optional;\n\n@FunctionalInte"
  },
  {
    "path": "5-0-functional-programming/5-3-1-crazy-optionals/src/test/java/com/bobocode/fp/CrazyOptionalsTest.java",
    "chars": 15071,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.fp.exception.AccountNotFoundException;\n"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/README.MD",
    "chars": 1006,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/pom.xml",
    "chars": 534,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/OOSumOfPrimes.java",
    "chars": 1053,
    "preview": "package com.bobocode.fp;\n\n/**\n * This examples demonstrates how to calculate the sum of prime numbers using Object-Orien"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/src/main/java/com/bobocode/fp/PrimeNumbers.java",
    "chars": 3483,
    "preview": "package com.bobocode.fp;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.List;\nimport java.ut"
  },
  {
    "path": "5-0-functional-programming/5-4-1-fun-prime-numbers/src/test/java/com/bobocode/fp/PrimeNumbersTest.java",
    "chars": 3875,
    "preview": "package com.bobocode.fp;\n\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.jun"
  },
  {
    "path": "5-0-functional-programming/README.md",
    "chars": 2542,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/pom.xml",
    "chars": 1120,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/CrazyLambdaExample.java",
    "chars": 1277,
    "preview": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport j"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/FunctionComposition.java",
    "chars": 1697,
    "preview": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.f"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaAndMethodReference.java",
    "chars": 1034,
    "preview": "package com.bobocode.lambdas.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaComparatorExample.java",
    "chars": 1095,
    "preview": "package com.bobocode.lambdas.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/LambdaRunnableExample.java",
    "chars": 710,
    "preview": "package com.bobocode.lambdas.tutorial;\n\npublic class LambdaRunnableExample {\n    public static void main(String[] args) "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/MethodRefToUpperCaseExample.java",
    "chars": 1777,
    "preview": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport j"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/MethodReferenceExamples.java",
    "chars": 1168,
    "preview": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.function.IntBinaryOperator;\nimport java.util.function.IntUnaryO"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/PredefinedInterfacePrimitives.java",
    "chars": 1172,
    "preview": "package com.bobocode.lambdas.tutorial;\n\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.function.DoubleS"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/lambdas/tutorial/PredefinedInterfacesExamples.java",
    "chars": 2378,
    "preview": "package com.bobocode.lambdas.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalCreation.java",
    "chars": 498,
    "preview": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalExamples.java",
    "chars": 2125,
    "preview": "package com.bobocode.optionals.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalImperativeVsDeclarativeCheck.java",
    "chars": 1131,
    "preview": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalMapping.java",
    "chars": 2666,
    "preview": "package com.bobocode.optionals.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\nimport "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalPrimitives.java",
    "chars": 919,
    "preview": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalReturningMethod.java",
    "chars": 1332,
    "preview": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalSearchByEmailExample.java",
    "chars": 1521,
    "preview": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/OptionalStream.java",
    "chars": 1049,
    "preview": "package com.bobocode.optionals.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.CreditAccount;\n\ni"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/optionals/tutorial/README.MD",
    "chars": 1994,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/ImperativeVsDeclarativeFiltering.java",
    "chars": 1083,
    "preview": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/ImperativeVsDeclarativeMax.java",
    "chars": 1320,
    "preview": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/README.MD",
    "chars": 2378,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamAdditionalFeatures.java",
    "chars": 2466,
    "preview": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamBasics.java",
    "chars": 1453,
    "preview": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamCollecting.java",
    "chars": 4098,
    "preview": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport j"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamFiltering.java",
    "chars": 750,
    "preview": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport j"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamMapping.java",
    "chars": 2310,
    "preview": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport j"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamParallelProcessing.java",
    "chars": 1061,
    "preview": "package com.bobocode.streams.tutorial;\n\n\nimport java.util.function.LongPredicate;\nimport java.util.stream.LongStream;\n\np"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamPrimitives.java",
    "chars": 1518,
    "preview": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport j"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamReducing.java",
    "chars": 1334,
    "preview": "package com.bobocode.streams.tutorial;\n\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport j"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamSideEffectFilteringExample.java",
    "chars": 528,
    "preview": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "5-0-functional-programming/src/main/java/com/bobocode/streams/tutorial/StreamWhileExample.java",
    "chars": 1285,
    "preview": "package com.bobocode.streams.tutorial;\n\nimport com.bobocode.data.Accounts;\nimport com.bobocode.model.Account;\n\nimport ja"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/README.md",
    "chars": 1759,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/pom.xml",
    "chars": 523,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/LinkedStack.java",
    "chars": 569,
    "preview": "package com.bobocode.tdd;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\npublic class LinkedStack<T> implemen"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/src/main/java/com/bobocode/tdd/Stack.java",
    "chars": 751,
    "preview": "package com.bobocode.tdd;\n\n/**\n *\n * Stack is a data structure that follows \"last in, first out\" rule (LIFO).\n */\npublic"
  },
  {
    "path": "6-0-test-driven-development/6-1-1-stack/src/test/java/com/bobocode/tdd/StackTest.java",
    "chars": 54,
    "preview": "package com.bobocode.tdd;\n\npublic class StackTest {\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/README.MD",
    "chars": 2095,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/pom.xml",
    "chars": 529,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/src/main/java/com/bobocode/tdd/LinkedList.java",
    "chars": 4207,
    "preview": "package com.bobocode.tdd;\n\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\n/**\n * {@link LinkedList} is a list"
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/src/main/java/com/bobocode/tdd/List.java",
    "chars": 337,
    "preview": "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  "
  },
  {
    "path": "6-0-test-driven-development/6-1-2-linked-list/src/test/java/com/bobocode/tdd/LinkedListTest.java",
    "chars": 59,
    "preview": "package com.bobocode.tdd;\n\npublic class LinkedListTest {\n}\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/README.md",
    "chars": 1840,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/pom.xml",
    "chars": 536,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/BinarySearchTree.java",
    "chars": 793,
    "preview": "package com.bobocode.tdd;\n\nimport java.util.function.Consumer;\n\npublic interface BinarySearchTree<T extends Comparable> "
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/src/main/java/com/bobocode/tdd/RecursiveBinarySearchTree.java",
    "chars": 911,
    "preview": "package com.bobocode.tdd;\n\nimport com.bobocode.util.ExerciseNotCompletedException;\n\nimport java.util.function.Consumer;\n"
  },
  {
    "path": "6-0-test-driven-development/6-1-3-binary-search-tree/src/test/java/com/bobocode/tdd/BinarySearchTreeTest.java",
    "chars": 59,
    "preview": "package com.bobocode.tdd;\n\nclass BinarySearchTreeTest {\n\n}\n"
  },
  {
    "path": "6-0-test-driven-development/README.md",
    "chars": 494,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "6-0-test-driven-development/pom.xml",
    "chars": 995,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "CONTRIBUTING.MD",
    "chars": 4301,
    "preview": "# Contributing to Java Fundamentals Course\n\nThank you for taking the time to contribute to the open-source education! 👏\n"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 1729,
    "preview": "# <img src=\"https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png\" "
  },
  {
    "path": "java-fundamentals-util/pom.xml",
    "chars": 747,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/data/Accounts.java",
    "chars": 2785,
    "preview": "package com.bobocode.data;\n\nimport com.bobocode.model.Account;\nimport com.bobocode.model.CreditAccount;\nimport com.boboc"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/model/Account.java",
    "chars": 503,
    "preview": "package com.bobocode.model;\n\nimport lombok.*;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\n\n@NoArgsConstruc"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/model/CreditAccount.java",
    "chars": 475,
    "preview": "package com.bobocode.model;\n\nimport lombok.AccessLevel;\nimport lombok.AllArgsConstructor;\nimport lombok.NoArgsConstructo"
  },
  {
    "path": "java-fundamentals-util/src/main/java/com/bobocode/model/Sex.java",
    "chars": 70,
    "preview": "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",
    "chars": 478,
    "preview": "package com.bobocode.util;\n\n/**\n * This is a custom exception that we throw in every method which should be implemented "
  },
  {
    "path": "lesson-demo/pom.xml",
    "chars": 753,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "lesson-demo/src/main/java/com/bobocode/DemoApp.java",
    "chars": 99,
    "preview": "package com.bobocode;\n\npublic class DemoApp {\n    public static void main(String[] args) {\n    }\n}\n"
  },
  {
    "path": "mvnw",
    "chars": 10069,
    "preview": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Softwa"
  },
  {
    "path": "mvnw.cmd",
    "chars": 6607,
    "preview": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software F"
  },
  {
    "path": "pom.xml",
    "chars": 4302,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the bobocode-projects/java-fundamentals-exercises GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 201 files (503.6 KB), approximately 130.9k tokens, and a symbol index with 1067 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!