diff --git a/.gitignore b/.gitignore index 188fae84..6e4b2269 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -.idea -out *.iml +.gradle +.idea +build +lib log - - - +out diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..daca46ff --- /dev/null +++ b/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'java' + id 'idea' +} + +group = 'ru.topjava.webapp' +version '1.0' + +repositories { + mavenLocal() + mavenCentral() +// maven { url "http://artapp/artifactory/public/" } +} + +sourceSets { + test.java.srcDir 'test' + + main { + java { + srcDirs = ['src'] + } + } +} + +configurations{ + implementation { + transitive = true + } +} + +dependencies { + implementation 'junit:junit:4.13.1' +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..77a56e49 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip +#distributionUrl=http\://artapp/artifactory/gradle-wrapper-remote-cache/distributions/gradle-6.7.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +file.encoding=utf-8 diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..3f4e36b5 --- /dev/null +++ b/gradlew @@ -0,0 +1,186 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +GRADLE_OPTS="-Dfile.encoding=utf-8" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..900fca17 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" +set GRADLE_OPTS="-Dfile.encoding=utf-8" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lesson/lesson1.md b/lesson/lesson1.md index a1b848e6..5e077974 100644 --- a/lesson/lesson1.md +++ b/lesson/lesson1.md @@ -88,7 +88,7 @@ r1, r2, r3,..., rn, null, null,..., null <----- size -----> <------- storage.length (10000) -------> ``` -- Протестируйте вашу реализацию с помощью классов `MainArray.main()` и `MainTestArrayStorage.main()` +- Протестируйте вашу реализацию с помощью классов `ru.topjava.webapp.MainArray.main()` и `ru.topjava.webapp.MainTestArrayStorage.main()` - Изучите дополнительные материалы по IntelliJ IDEA: - [Idea Wiki](https://github.com/JavaOPs/topjava/wiki/IDEA) ([поставьте кодировку UTF-8](https://github.com/JavaOPs/topjava/wiki/IDEA#Поставить-кодировку-utf-8), [поменяйте шрифт по умолчанию на DejaVu](https://github.com/JavaOPs/topjava/wiki/IDEA#Поменять-фонт-по-умолчанию-dejavu)) - [Руководство пользователя IntelliJ IDEA. Отладчик](http://info.javarush.ru/idea_help/2014/01/22/Руководство-пользователя-IntelliJ-IDEA-Отладчик-.html) diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..4e04e401 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "basejava" diff --git a/src/ArrayStorage.java b/src/ArrayStorage.java deleted file mode 100644 index 7aff0388..00000000 --- a/src/ArrayStorage.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Array based storage for Resumes - */ -public class ArrayStorage { - Resume[] storage = new Resume[10000]; - - void clear() { - } - - void save(Resume r) { - } - - Resume get(String uuid) { - return null; - } - - void delete(String uuid) { - } - - /** - * @return array, contains only Resumes in storage (without null) - */ - Resume[] getAll() { - return new Resume[0]; - } - - int size() { - return 0; - } -} diff --git a/src/MainArray.java b/src/MainArray.java deleted file mode 100644 index b1cb33b7..00000000 --- a/src/MainArray.java +++ /dev/null @@ -1,71 +0,0 @@ -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -/** - * Interactive test for ArrayStorage implementation - * (just run, no need to understand) - */ -public class MainArray { - private final static ArrayStorage ARRAY_STORAGE = new ArrayStorage(); - - public static void main(String[] args) throws IOException { - BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); - Resume r; - while (true) { - System.out.print("Введите одну из команд - (list | save uuid | delete uuid | get uuid | clear | exit): "); - String[] params = reader.readLine().trim().toLowerCase().split(" "); - if (params.length < 1 || params.length > 2) { - System.out.println("Неверная команда."); - continue; - } - String uuid = null; - if (params.length == 2) { - uuid = params[1].intern(); - } - switch (params[0]) { - case "list": - printAll(); - break; - case "size": - System.out.println(ARRAY_STORAGE.size()); - break; - case "save": - r = new Resume(); - r.uuid = uuid; - ARRAY_STORAGE.save(r); - printAll(); - break; - case "delete": - ARRAY_STORAGE.delete(uuid); - printAll(); - break; - case "get": - System.out.println(ARRAY_STORAGE.get(uuid)); - break; - case "clear": - ARRAY_STORAGE.clear(); - printAll(); - break; - case "exit": - return; - default: - System.out.println("Неверная команда."); - break; - } - } - } - - static void printAll() { - Resume[] all = ARRAY_STORAGE.getAll(); - System.out.println("----------------------------"); - if (all.length == 0) { - System.out.println("Empty"); - } else { - for (Resume r : all) { - System.out.println(r); - } - } - System.out.println("----------------------------"); - } -} \ No newline at end of file diff --git a/src/MainTestArrayStorage.java b/src/MainTestArrayStorage.java deleted file mode 100644 index b15b81e2..00000000 --- a/src/MainTestArrayStorage.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Test for your ArrayStorage implementation - */ -public class MainTestArrayStorage { - static final ArrayStorage ARRAY_STORAGE = new ArrayStorage(); - - public static void main(String[] args) { - Resume r1 = new Resume(); - r1.uuid = "uuid1"; - Resume r2 = new Resume(); - r2.uuid = "uuid2"; - Resume r3 = new Resume(); - r3.uuid = "uuid3"; - - ARRAY_STORAGE.save(r1); - ARRAY_STORAGE.save(r2); - ARRAY_STORAGE.save(r3); - - System.out.println("Get r1: " + ARRAY_STORAGE.get(r1.uuid)); - System.out.println("Size: " + ARRAY_STORAGE.size()); - - System.out.println("Get dummy: " + ARRAY_STORAGE.get("dummy")); - - printAll(); - ARRAY_STORAGE.delete(r1.uuid); - printAll(); - ARRAY_STORAGE.clear(); - printAll(); - - System.out.println("Size: " + ARRAY_STORAGE.size()); - } - - static void printAll() { - System.out.println("\nGet All"); - for (Resume r : ARRAY_STORAGE.getAll()) { - System.out.println(r); - } - } -} diff --git a/src/Resume.java b/src/Resume.java deleted file mode 100644 index 8de4e4b8..00000000 --- a/src/Resume.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Initial resume class - */ -public class Resume { - - // Unique identifier - String uuid; - - @Override - public String toString() { - return uuid; - } -} diff --git a/src/ru/topjava/webapp/MainArray.java b/src/ru/topjava/webapp/MainArray.java new file mode 100644 index 00000000..ff3f8cd8 --- /dev/null +++ b/src/ru/topjava/webapp/MainArray.java @@ -0,0 +1,107 @@ +package ru.topjava.webapp; + +import ru.topjava.webapp.model.Resume; +import ru.topjava.webapp.storage.ArrayStorage; +import ru.topjava.webapp.storage.SortedArrayStorage; +import ru.topjava.webapp.storage.Storage; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * Interactive test for ArrayStorage implementation + * (just run, no need to understand) + */ +public class MainArray { + private static Storage arrayStorage; + + public static void main(String[] args) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + Resume resume; + String[] params; + storageChoice: + while (true) { + System.out.print("Выберите тип хранилища - (sort | unsort | exit): "); + params = reader.readLine().trim().toLowerCase().split(" "); + if (params.length < 1 || params.length > 2) { + System.out.println("Неверная команда."); + continue; + } + switch (params[0]) { + case "sort": + System.out.println("Выбранный тип хранилища: отсортированный массив."); + arrayStorage = new SortedArrayStorage(); + break storageChoice; + case "unsort": + System.out.println("Выбранный тип хранилища: неотсортированный массив."); + arrayStorage = new ArrayStorage(); + break storageChoice; + case "exit": + return; + default: + System.out.println("Неверная команда."); + break; + } + } + while (true) { + System.out.print("Введите одну из команд - (list | save uuid | update uuid | delete uuid | get uuid | clear | exit): "); + params = reader.readLine().trim().toLowerCase().split(" "); + if (params.length < 1 || params.length > 2) { + System.out.println("Неверная команда."); + continue; + } + String uuid = null; + if (params.length == 2) { + uuid = params[1].intern(); + } + switch (params[0]) { + case "list": + printAll(); + break; + case "size": + System.out.println(arrayStorage.size()); + break; + case "save": + resume = new Resume(uuid); + arrayStorage.save(resume); + printAll(); + break; + case "update": + resume = new Resume(uuid); + arrayStorage.update(resume); + printAll(); + break; + case "delete": + arrayStorage.delete(uuid); + printAll(); + break; + case "get": + System.out.println(arrayStorage.get(uuid)); + break; + case "clear": + arrayStorage.clear(); + printAll(); + break; + case "exit": + return; + default: + System.out.println("Неверная команда."); + break; + } + } + } + + static void printAll() { + Resume[] all = arrayStorage.getAll(); + System.out.println("----------------------------"); + if (all.length == 0) { + System.out.println("Empty"); + } else { + for (Resume resume : all) { + System.out.println(resume); + } + } + System.out.println("----------------------------"); + } +} \ No newline at end of file diff --git a/src/ru/topjava/webapp/MainReflection.java b/src/ru/topjava/webapp/MainReflection.java new file mode 100644 index 00000000..66613b5f --- /dev/null +++ b/src/ru/topjava/webapp/MainReflection.java @@ -0,0 +1,14 @@ +package ru.topjava.webapp; + +import ru.topjava.webapp.model.Resume; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class MainReflection { + public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Resume resume = new Resume("test_reflection"); + Method toStringMethod = resume.getClass().getMethod("toString"); + System.out.println(toStringMethod.invoke(resume)); + } +} diff --git a/src/ru/topjava/webapp/MainTestArrayStorage.java b/src/ru/topjava/webapp/MainTestArrayStorage.java new file mode 100644 index 00000000..2a01664b --- /dev/null +++ b/src/ru/topjava/webapp/MainTestArrayStorage.java @@ -0,0 +1,54 @@ +package ru.topjava.webapp; + +import ru.topjava.webapp.model.Resume; +import ru.topjava.webapp.storage.SortedArrayStorage; +import ru.topjava.webapp.storage.Storage; + +import java.util.Arrays; + +/** + * Test for your ArrayStorage implementation + */ +public class MainTestArrayStorage { + static final Storage ARRAY_STORAGE = new SortedArrayStorage(); + + public static void main(String[] args) { + Resume resume1 = new Resume("uuid1"); + Resume resume3 = new Resume("uuid3"); + Resume resume2 = new Resume("uuid2"); + Resume resume3Update = new Resume("uuid3"); + Resume resume4 = new Resume("uuid4"); + + ARRAY_STORAGE.save(resume1); + ARRAY_STORAGE.save(resume2); + ARRAY_STORAGE.save(resume3); + + System.out.println("Get resume1: " + ARRAY_STORAGE.get(resume1.getUuid())); + System.out.println("Size: " + ARRAY_STORAGE.size()); + + System.out.println("Get dummy: " + ARRAY_STORAGE.get("dummy")); + + ARRAY_STORAGE.update(resume3Update); + System.out.println("Update resume3: " + (ARRAY_STORAGE.get("uuid3") == resume3Update)); + System.out.println("Update non-existing resume4"); + ARRAY_STORAGE.update(resume4); + + System.out.println("resume2 index: " + Arrays.binarySearch(ARRAY_STORAGE.getAll(), resume2)); + printAll(); + ARRAY_STORAGE.delete(resume2.getUuid()); + printAll(); + System.out.println("resume2 index: " + Arrays.binarySearch(ARRAY_STORAGE.getAll(), resume2)); + ARRAY_STORAGE.clear(); + printAll(); + System.out.println("resume2 index: " + Arrays.binarySearch(ARRAY_STORAGE.getAll(), resume2)); + + System.out.println("Size: " + ARRAY_STORAGE.size()); + } + + static void printAll() { + System.out.println("\nGet All"); + for (Resume r : ARRAY_STORAGE.getAll()) { + System.out.println(r); + } + } +} diff --git a/src/ru/topjava/webapp/exception/ExistStorageException.java b/src/ru/topjava/webapp/exception/ExistStorageException.java new file mode 100644 index 00000000..23abe86b --- /dev/null +++ b/src/ru/topjava/webapp/exception/ExistStorageException.java @@ -0,0 +1,7 @@ +package ru.topjava.webapp.exception; + +public class ExistStorageException extends StorageException { + public ExistStorageException(String uuid) { + super(String.format("uuid '%1$s' is already exists", uuid), uuid); + } +} diff --git a/src/ru/topjava/webapp/exception/NotExistStorageException.java b/src/ru/topjava/webapp/exception/NotExistStorageException.java new file mode 100644 index 00000000..9257fab3 --- /dev/null +++ b/src/ru/topjava/webapp/exception/NotExistStorageException.java @@ -0,0 +1,7 @@ +package ru.topjava.webapp.exception; + +public class NotExistStorageException extends StorageException { + public NotExistStorageException(String uuid) { + super(String.format("uuid '%1$s' is not exists", uuid), uuid); + } +} diff --git a/src/ru/topjava/webapp/exception/StorageException.java b/src/ru/topjava/webapp/exception/StorageException.java new file mode 100644 index 00000000..864a4c54 --- /dev/null +++ b/src/ru/topjava/webapp/exception/StorageException.java @@ -0,0 +1,14 @@ +package ru.topjava.webapp.exception; + +public class StorageException extends RuntimeException{ + private final String uuid; + + public StorageException(String message, String uuid) { + super(message); + this.uuid = uuid; + } + + public String getUuid() { + return uuid; + } +} diff --git a/src/ru/topjava/webapp/model/Resume.java b/src/ru/topjava/webapp/model/Resume.java new file mode 100644 index 00000000..eea592b6 --- /dev/null +++ b/src/ru/topjava/webapp/model/Resume.java @@ -0,0 +1,49 @@ +package ru.topjava.webapp.model; + +import java.util.UUID; + +/** + * Initial resume class + */ +public class Resume implements Comparable { + + // Unique identifier + private final String uuid; + + public Resume() { + this(UUID.randomUUID().toString()); + } + + public Resume(String uuid) { + this.uuid = uuid; + } + + public String getUuid() { + return uuid; + } + + @Override + public String toString() { + return uuid; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + + Resume resume = (Resume) object; + + return uuid.equals(resume.uuid); + } + + @Override + public int hashCode() { + return uuid.hashCode(); + } + + @Override + public int compareTo(Resume resume) { + return uuid.compareTo(resume.uuid); + } +} diff --git a/src/ru/topjava/webapp/storage/AbstractArrayStorage.java b/src/ru/topjava/webapp/storage/AbstractArrayStorage.java new file mode 100644 index 00000000..4a26b053 --- /dev/null +++ b/src/ru/topjava/webapp/storage/AbstractArrayStorage.java @@ -0,0 +1,66 @@ +package ru.topjava.webapp.storage; + +import ru.topjava.webapp.exception.StorageException; +import ru.topjava.webapp.model.Resume; + +import java.util.Arrays; + +/** + * Array based storage for Resumes + */ +public abstract class AbstractArrayStorage extends AbstractStorage { + protected static final int STORAGE_LENGTH = 10000; + protected final Resume[] storage = new Resume[STORAGE_LENGTH]; + protected int size = 0; + + @Override + public void clear() { + Arrays.fill(storage, 0, size, null); + size = 0; + } + + private void checkStorageIsNotFull(String uuid) { + if (size >= STORAGE_LENGTH) { + throw new StorageException("storage is full", uuid); + } + } + + protected abstract void saveByIndexToArray(int resumeIndex, Resume resume); + + @Override + protected void saveByKey(Object resumeKey, Resume resume) { + final String uuid = resume.getUuid(); + checkStorageIsNotFull(uuid); + saveByIndexToArray((int) resumeKey, resume); + size++; + } + + @Override + protected Resume getFromStorage(Object resumeKey) { + return storage[(int) resumeKey]; + } + + protected abstract void deleteByIndexFromArray(int resumeIndex); + + @Override + protected void deleteByKey(Object resumeKey) { + deleteByIndexFromArray((int) resumeKey); + storage[size - 1] = null; + size--; + } + + @Override + public Resume[] getAll() { + return Arrays.copyOf(storage, size); + } + + @Override + public int size() { + return size; + } + + @Override + protected void updateInStorage(Object resumeKey, Resume resume) { + storage[(int) resumeKey] = resume; + } +} diff --git a/src/ru/topjava/webapp/storage/AbstractStorage.java b/src/ru/topjava/webapp/storage/AbstractStorage.java new file mode 100644 index 00000000..04ff56d7 --- /dev/null +++ b/src/ru/topjava/webapp/storage/AbstractStorage.java @@ -0,0 +1,56 @@ +package ru.topjava.webapp.storage; + +import ru.topjava.webapp.exception.ExistStorageException; +import ru.topjava.webapp.exception.NotExistStorageException; +import ru.topjava.webapp.model.Resume; + +public abstract class AbstractStorage implements Storage { + + protected abstract Object getKey(String uuid); + + protected abstract void saveByKey(Object resumeKey, Resume resume); + + protected abstract void deleteByKey(Object resumeKey); + + protected boolean isResumeExists(Object resumeKey) { + return ((int) resumeKey >= 0); + } + + private Object findKeyIfResumeNotExist(String uuid) { + final Object resumeKey = getKey(uuid); + if (isResumeExists(resumeKey)) { + throw new ExistStorageException(uuid); + } + return resumeKey; + } + + private Object findKeyIfResumeExist(String uuid) { + final Object resumeKey = getKey(uuid); + if (!isResumeExists(resumeKey)) { + throw new NotExistStorageException(uuid); + } + return resumeKey; + } + + public final void save(Resume resume) { + final String uuid = resume.getUuid(); + saveByKey(findKeyIfResumeNotExist(uuid), resume); + } + + protected abstract Resume getFromStorage(Object resumeKey); + + public final Resume get(String uuid) { + return getFromStorage(findKeyIfResumeExist(uuid)); + } + + public final void delete(String uuid) { + deleteByKey(findKeyIfResumeExist(uuid)); + } + + protected abstract void updateInStorage(Object resumeKey, Resume resume); + + public final void update(Resume resume) { + final String uuid = resume.getUuid(); + updateInStorage(findKeyIfResumeExist(uuid), resume); + } +} diff --git a/src/ru/topjava/webapp/storage/ArrayStorage.java b/src/ru/topjava/webapp/storage/ArrayStorage.java new file mode 100644 index 00000000..9b5269de --- /dev/null +++ b/src/ru/topjava/webapp/storage/ArrayStorage.java @@ -0,0 +1,28 @@ +package ru.topjava.webapp.storage; + +import ru.topjava.webapp.model.Resume; + +/** + * Unsorted array based storage for Resumes + */ +public class ArrayStorage extends AbstractArrayStorage { + @Override + protected Object getKey(String uuid) { + for (int i = 0; i < size; i++) { + if (storage[i].getUuid().equals(uuid)) { + return i; + } + } + return -1; + } + + @Override + protected void saveByIndexToArray(int resumeIndex, Resume resume) { + storage[size] = resume; + } + + @Override + protected void deleteByIndexFromArray(int resumeIndex) { + storage[resumeIndex] = storage[size - 1]; + } +} diff --git a/src/ru/topjava/webapp/storage/ListStorage.java b/src/ru/topjava/webapp/storage/ListStorage.java new file mode 100644 index 00000000..9884b75c --- /dev/null +++ b/src/ru/topjava/webapp/storage/ListStorage.java @@ -0,0 +1,56 @@ +package ru.topjava.webapp.storage; + +import ru.topjava.webapp.model.Resume; + +import java.util.ArrayList; +import java.util.List; + +public class ListStorage extends AbstractStorage { + private final List storage = new ArrayList<>(); + + @Override + protected Object getKey(String uuid) { + for (int i = 0; i < storage.size(); i++) { + if (storage.get(i).getUuid().equals(uuid)) { + return i; + } + } + return -1; + } + + @Override + protected void saveByKey(Object resumeKey, Resume resume) { + storage.add(resume); + } + + @Override + protected void deleteByKey(Object resumeKey) { + storage.remove((int) resumeKey); + } + + @Override + public void clear() { + storage.clear(); + } + + @Override + protected Resume getFromStorage(Object resumeKey) { + return storage.get((int) resumeKey); + } + + @Override + public Resume[] getAll() { + final Resume[] tempArray = new Resume[this.size()]; + return storage.toArray(tempArray); + } + + @Override + public int size() { + return storage.size(); + } + + @Override + protected void updateInStorage(Object resumeKey, Resume resume) { + storage.set((int) resumeKey, resume); + } +} diff --git a/src/ru/topjava/webapp/storage/MapStorage.java b/src/ru/topjava/webapp/storage/MapStorage.java new file mode 100644 index 00000000..86319a3b --- /dev/null +++ b/src/ru/topjava/webapp/storage/MapStorage.java @@ -0,0 +1,56 @@ +package ru.topjava.webapp.storage; + +import ru.topjava.webapp.model.Resume; + +import java.util.HashMap; +import java.util.Map; + +public class MapStorage extends AbstractStorage { + private final Map storage = new HashMap<>(); + + @Override + protected Object getKey(String uuid) { + return uuid; + } + + @Override + protected void saveByKey(Object uuid, Resume resume) { + storage.put((String) uuid, resume); + } + + @Override + protected void deleteByKey(Object uuid) { + storage.remove((String) uuid); + } + + @Override + protected boolean isResumeExists(Object uuid) { + return storage.containsKey((String) uuid); + } + + @Override + public void clear() { + storage.clear(); + } + + @Override + protected Resume getFromStorage(Object uuid) { + return storage.get((String) uuid); + } + + @Override + public Resume[] getAll() { + final Resume[] tempArray = new Resume[this.size()]; + return storage.values().toArray(tempArray); + } + + @Override + public int size() { + return storage.size(); + } + + @Override + protected void updateInStorage(Object uuid, Resume resume) { + storage.replace((String) uuid, resume); + } +} diff --git a/src/ru/topjava/webapp/storage/SortedArrayStorage.java b/src/ru/topjava/webapp/storage/SortedArrayStorage.java new file mode 100644 index 00000000..4b23a22a --- /dev/null +++ b/src/ru/topjava/webapp/storage/SortedArrayStorage.java @@ -0,0 +1,28 @@ +package ru.topjava.webapp.storage; + +import ru.topjava.webapp.model.Resume; + +import java.util.Arrays; + +/** + * Sorted array based storage for Resumes + */ +public class SortedArrayStorage extends AbstractArrayStorage { + @Override + protected Object getKey(String uuid) { + final Resume resumeToSearch = new Resume(uuid); + return Arrays.binarySearch(storage, 0, size, resumeToSearch); + } + + @Override + protected void saveByIndexToArray(int resumeIndex, Resume resume) { + final int resumeIndexToSave = -resumeIndex - 1; + System.arraycopy(storage, resumeIndexToSave, storage, resumeIndexToSave + 1, size - resumeIndexToSave); + storage[resumeIndexToSave] = resume; + } + + @Override + protected void deleteByIndexFromArray(int resumeIndex) { + System.arraycopy(storage, resumeIndex + 1, storage, resumeIndex, size - resumeIndex - 1); + } +} diff --git a/src/ru/topjava/webapp/storage/Storage.java b/src/ru/topjava/webapp/storage/Storage.java new file mode 100644 index 00000000..749438a3 --- /dev/null +++ b/src/ru/topjava/webapp/storage/Storage.java @@ -0,0 +1,26 @@ +package ru.topjava.webapp.storage; + +import ru.topjava.webapp.model.Resume; + +/** + * Array based storage for Resumes + */ +public interface Storage { + + void clear(); + + void save(Resume resume); + + Resume get(String uuid); + + void delete(String uuid); + + /** + * @return array, contains only Resumes in storage (without null) + */ + Resume[] getAll(); + + int size(); + + void update(Resume resume); +} diff --git a/test/ru/topjava/webapp/storage/AbstractArrayStorageTest.java b/test/ru/topjava/webapp/storage/AbstractArrayStorageTest.java new file mode 100644 index 00000000..224ee3ee --- /dev/null +++ b/test/ru/topjava/webapp/storage/AbstractArrayStorageTest.java @@ -0,0 +1,28 @@ +package ru.topjava.webapp.storage; + +import org.junit.Assert; +import org.junit.Test; +import ru.topjava.webapp.exception.StorageException; +import ru.topjava.webapp.model.Resume; + +import static ru.topjava.webapp.storage.AbstractArrayStorage.STORAGE_LENGTH; + +public abstract class AbstractArrayStorageTest extends AbstractStorageTest { + + public AbstractArrayStorageTest(Storage storage) { + super(storage); + } + + @Test(expected = StorageException.class) + public void saveStorageFull() { + int initialSize = storage.size(); + try { + for (int i = 0; i < STORAGE_LENGTH - initialSize; i++) { + storage.save(new Resume()); + } + } catch (StorageException e) { + Assert.fail("StorageException thrown earlier, than expected"); + } + storage.save(new Resume()); + } +} \ No newline at end of file diff --git a/test/ru/topjava/webapp/storage/AbstractStorageTest.java b/test/ru/topjava/webapp/storage/AbstractStorageTest.java new file mode 100644 index 00000000..7e4c6c9d --- /dev/null +++ b/test/ru/topjava/webapp/storage/AbstractStorageTest.java @@ -0,0 +1,102 @@ +package ru.topjava.webapp.storage; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import ru.topjava.webapp.exception.ExistStorageException; +import ru.topjava.webapp.exception.NotExistStorageException; +import ru.topjava.webapp.model.Resume; + +import java.util.Arrays; + +public abstract class AbstractStorageTest { + protected final Storage storage; + protected final Resume resume1; + protected final Resume resume2; + protected final Resume resume3; + protected final Resume resume4; + + public AbstractStorageTest(Storage storage) { + this.storage = storage; + resume1 = new Resume("uuid1"); + resume2 = new Resume("uuid2"); + resume3 = new Resume("uuid3"); + resume4 = new Resume("uuid4"); + } + + @Before + public void setUp() { + storage.clear(); + storage.save(resume1); + storage.save(resume2); + storage.save(resume3); + } + + @Test + public void save() { + storage.save(resume4); + Assert.assertEquals(resume4, storage.get("uuid4")); + Assert.assertEquals(4, storage.size()); + } + + @Test(expected = ExistStorageException.class) + public void saveExist() { + storage.save(resume2); + } + + @Test(expected = NotExistStorageException.class) + public void delete() { + try { + storage.delete(resume2.getUuid()); + Assert.assertEquals(2, storage.size()); + } catch (NotExistStorageException e) { + Assert.fail("NotExistStorageException thrown earlier, than expected"); + } + storage.get("uuid2"); + } + + @Test(expected = NotExistStorageException.class) + public void deleteNotExist() { + storage.delete(resume4.getUuid()); + } + + @Test + public void update() { + final Resume resume3Update = new Resume("uuid3"); + storage.update(resume3Update); + Assert.assertEquals(resume3Update, storage.get("uuid3")); + } + + @Test(expected = NotExistStorageException.class) + public void updateNotExist() { + storage.update(resume4); + } + + @Test + public void get() { + Assert.assertEquals(resume1, storage.get("uuid1")); + } + + @Test(expected = NotExistStorageException.class) + public void getNotExist() { + storage.get(resume4.getUuid()); + } + + @Test + public void getAll() { + final Resume[] expected = new Resume[]{resume1, resume2, resume3}; + final Resume[] actual = storage.getAll(); + Arrays.sort(actual); + Assert.assertArrayEquals(expected, actual); + } + + @Test + public void size() { + Assert.assertEquals(3, storage.size()); + } + + @Test + public void clear() { + storage.clear(); + } +} \ No newline at end of file diff --git a/test/ru/topjava/webapp/storage/ArrayStorageTest.java b/test/ru/topjava/webapp/storage/ArrayStorageTest.java new file mode 100644 index 00000000..8d56dc60 --- /dev/null +++ b/test/ru/topjava/webapp/storage/ArrayStorageTest.java @@ -0,0 +1,8 @@ +package ru.topjava.webapp.storage; + +public class ArrayStorageTest extends AbstractArrayStorageTest { + + public ArrayStorageTest() { + super(new ArrayStorage()); + } +} \ No newline at end of file diff --git a/test/ru/topjava/webapp/storage/ListStorageTest.java b/test/ru/topjava/webapp/storage/ListStorageTest.java new file mode 100644 index 00000000..3db08fb5 --- /dev/null +++ b/test/ru/topjava/webapp/storage/ListStorageTest.java @@ -0,0 +1,8 @@ +package ru.topjava.webapp.storage; + +public class ListStorageTest extends AbstractStorageTest { + + public ListStorageTest() { + super(new ListStorage()); + } +} \ No newline at end of file diff --git a/test/ru/topjava/webapp/storage/MapStorageTest.java b/test/ru/topjava/webapp/storage/MapStorageTest.java new file mode 100644 index 00000000..5b797ba3 --- /dev/null +++ b/test/ru/topjava/webapp/storage/MapStorageTest.java @@ -0,0 +1,8 @@ +package ru.topjava.webapp.storage; + +public class MapStorageTest extends AbstractStorageTest { + + public MapStorageTest() { + super(new MapStorage()); + } +} \ No newline at end of file diff --git a/test/ru/topjava/webapp/storage/SortedArrayStorageTest.java b/test/ru/topjava/webapp/storage/SortedArrayStorageTest.java new file mode 100644 index 00000000..3812fe52 --- /dev/null +++ b/test/ru/topjava/webapp/storage/SortedArrayStorageTest.java @@ -0,0 +1,8 @@ +package ru.topjava.webapp.storage; + +public class SortedArrayStorageTest extends AbstractArrayStorageTest { + + public SortedArrayStorageTest() { + super(new SortedArrayStorage()); + } +} \ No newline at end of file