diff --git a/09-Serialization/.idea/compiler.xml b/09-Serialization/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/09-Serialization/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/09-Serialization/.idea/copyright/profiles_settings.xml b/09-Serialization/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/09-Serialization/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/09-Serialization/.idea/misc.xml b/09-Serialization/.idea/misc.xml
new file mode 100644
index 0000000..800fcd0
--- /dev/null
+++ b/09-Serialization/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/compiler.xml b/10-DB/.idea/compiler.xml
new file mode 100644
index 0000000..ccb47a6
--- /dev/null
+++ b/10-DB/.idea/compiler.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/copyright/profiles_settings.xml b/10-DB/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/10-DB/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/gradle.xml b/10-DB/.idea/gradle.xml
new file mode 100644
index 0000000..ab6e46d
--- /dev/null
+++ b/10-DB/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__cglib_cglib_nodep_2_2_2.xml b/10-DB/.idea/libraries/Gradle__cglib_cglib_nodep_2_2_2.xml
new file mode 100644
index 0000000..eb17151
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__cglib_cglib_nodep_2_2_2.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__com_thoughtworks_proxytoys_proxytoys_1_0.xml b/10-DB/.idea/libraries/Gradle__com_thoughtworks_proxytoys_proxytoys_1_0.xml
new file mode 100644
index 0000000..b1005b8
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__com_thoughtworks_proxytoys_proxytoys_1_0.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__junit_junit_4_11.xml b/10-DB/.idea/libraries/Gradle__junit_junit_4_11.xml
new file mode 100644
index 0000000..dc26b34
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__junit_junit_4_11.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml b/10-DB/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml
new file mode 100644
index 0000000..8262f72
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__org_jetbrains_annotations_15_0.xml b/10-DB/.idea/libraries/Gradle__org_jetbrains_annotations_15_0.xml
new file mode 100644
index 0000000..ca78bcd
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__org_jetbrains_annotations_15_0.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__org_mongodb_bson_3_4_0.xml b/10-DB/.idea/libraries/Gradle__org_mongodb_bson_3_4_0.xml
new file mode 100644
index 0000000..e525ce7
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__org_mongodb_bson_3_4_0.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__org_mongodb_mongo_java_driver_3_2_2.xml b/10-DB/.idea/libraries/Gradle__org_mongodb_mongo_java_driver_3_2_2.xml
new file mode 100644
index 0000000..82b40fb
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__org_mongodb_mongo_java_driver_3_2_2.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__org_mongodb_mongodb_driver_3_4_0.xml b/10-DB/.idea/libraries/Gradle__org_mongodb_mongodb_driver_3_4_0.xml
new file mode 100644
index 0000000..747f9ae
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__org_mongodb_mongodb_driver_3_4_0.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__org_mongodb_mongodb_driver_core_3_4_0.xml b/10-DB/.idea/libraries/Gradle__org_mongodb_mongodb_driver_core_3_4_0.xml
new file mode 100644
index 0000000..e0619e4
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__org_mongodb_mongodb_driver_core_3_4_0.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/libraries/Gradle__org_mongodb_morphia_morphia_1_2_1.xml b/10-DB/.idea/libraries/Gradle__org_mongodb_morphia_morphia_1_2_1.xml
new file mode 100644
index 0000000..34181a6
--- /dev/null
+++ b/10-DB/.idea/libraries/Gradle__org_mongodb_morphia_morphia_1_2_1.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/misc.xml b/10-DB/.idea/misc.xml
new file mode 100644
index 0000000..9793229
--- /dev/null
+++ b/10-DB/.idea/misc.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/modules.xml b/10-DB/.idea/modules.xml
new file mode 100644
index 0000000..76a11df
--- /dev/null
+++ b/10-DB/.idea/modules.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/modules/10-DB.iml b/10-DB/.idea/modules/10-DB.iml
new file mode 100644
index 0000000..cb2b1ee
--- /dev/null
+++ b/10-DB/.idea/modules/10-DB.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/modules/10-DB_main.iml b/10-DB/.idea/modules/10-DB_main.iml
new file mode 100644
index 0000000..1bbbb6e
--- /dev/null
+++ b/10-DB/.idea/modules/10-DB_main.iml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/.idea/modules/10-DB_test.iml b/10-DB/.idea/modules/10-DB_test.iml
new file mode 100644
index 0000000..b9b69ca
--- /dev/null
+++ b/10-DB/.idea/modules/10-DB_test.iml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-DB/build.gradle b/10-DB/build.gradle
new file mode 100644
index 0000000..9893ef0
--- /dev/null
+++ b/10-DB/build.gradle
@@ -0,0 +1,17 @@
+group 'ru.spbau.bachelor2015.veselov.hw10'
+version '1.0-SNAPSHOT'
+
+apply plugin: 'java'
+
+sourceCompatibility = 1.8
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ testCompile group: 'junit', name: 'junit', version: '4.11'
+ compile group: 'org.jetbrains', name: 'annotations', version: '15.0'
+ compile group: 'org.mongodb', name: 'mongodb-driver', version: '3.4.0'
+ compile group: 'org.mongodb.morphia', name: 'morphia', version: '1.2.1'
+}
diff --git a/10-DB/gradle/wrapper/gradle-wrapper.jar b/10-DB/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..ca78035
Binary files /dev/null and b/10-DB/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/10-DB/gradle/wrapper/gradle-wrapper.properties b/10-DB/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..c8ae6f7
--- /dev/null
+++ b/10-DB/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Nov 28 21:38:34 MSK 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip
diff --git a/10-DB/gradlew b/10-DB/gradlew
new file mode 100755
index 0000000..27309d9
--- /dev/null
+++ b/10-DB/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## 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=""
+
+# 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, switch paths to Windows format before running java
+if $cygwin ; 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=$((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
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/10-DB/gradlew.bat b/10-DB/gradlew.bat
new file mode 100644
index 0000000..f6d5974
--- /dev/null
+++ b/10-DB/gradlew.bat
@@ -0,0 +1,90 @@
+@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 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=
+
+@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 init
+
+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 init
+
+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
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+: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 %CMD_LINE_ARGS%
+
+: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/10-DB/settings.gradle b/10-DB/settings.gradle
new file mode 100644
index 0000000..4b63503
--- /dev/null
+++ b/10-DB/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = '10-DB'
+
diff --git a/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/DataBase.java b/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/DataBase.java
new file mode 100644
index 0000000..aecf172
--- /dev/null
+++ b/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/DataBase.java
@@ -0,0 +1,155 @@
+package ru.spbau.bachelor2015.veselov.hw10;
+
+import com.mongodb.MongoClient;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.mongodb.morphia.Datastore;
+import org.mongodb.morphia.Morphia;
+
+import java.util.List;
+
+/**
+ * A class which performs interaction with mongo database with a given name.
+ */
+public class DataBase {
+ private final Datastore datastore;
+
+ /**
+ * Creates a link to mongo database which has a given name.
+ *
+ * @param databaseName a database name.
+ */
+ public DataBase(String databaseName) {
+ Morphia morphia = new Morphia();
+ morphia.mapPackage("ru.spbau.bachelor2015.veselov.hw10");
+
+ datastore = morphia.createDatastore(new MongoClient(), databaseName);
+ }
+
+ /**
+ * Adds new entry to the database. Does nothing if entry with given name and phone already exists.
+ *
+ * @param name a person name.
+ * @param phone a phone number.
+ * @return ENTRY_ALREADY_EXISTS if entry already exists, OK otherwise.
+ */
+ public @NotNull Respond addNewEntry(@NotNull String name, @NotNull String phone) {
+ if (getEntry(name, phone) != null) {
+ return Respond.ENTRY_ALREADY_EXISTS;
+ }
+
+ datastore.save(new PhoneBookEntry(name, phone));
+ return Respond.OK;
+ }
+
+ /**
+ * Returns all entries with a given name.
+ *
+ * @param name a person name.
+ * @return a list of entries, where each entry's name equals to given one.
+ */
+ public @NotNull List findByName(@NotNull String name) {
+ return datastore.createQuery(PhoneBookEntry.class).field("name").equal(name).asList();
+ }
+
+ /**
+ * Returns all entries with a given phone.
+ *
+ * @param phone a phone number.
+ * @return a list of entries, where each entry's phone equals to given one.
+ */
+ public @NotNull List findByPhone(@NotNull String phone) {
+ return datastore.createQuery(PhoneBookEntry.class).field("phone").equal(phone).asList();
+ }
+
+ /**
+ * Deletes entry from the database.
+ *
+ * @param name a person name.
+ * @param phone a phone number.
+ * @return NO_SUCH_ENTRY if there is no entry with given name and phone in the database, OK otherwise.
+ */
+ public @NotNull Respond deleteEntry(@NotNull String name, @NotNull String phone) {
+ PhoneBookEntry entry = getEntry(name, phone);
+ if (entry == null) {
+ return Respond.NO_SUCH_ENTRY;
+ }
+
+ datastore.delete(entry);
+ return Respond.OK;
+ }
+
+ /**
+ * Changes person name of a particular entry which determined by it's name and phone fields.
+ *
+ * @param oldName old person name.
+ * @param phone a phone number.
+ * @param newName new person name.
+ * @return NO_SUCH_ENTRY if there is no entry with given old name and phone in the database, ENTRY_ALREADY_EXISTS if
+ * there is an entry with given new name and phone in the database, OK otherwise.
+ */
+ public @NotNull Respond changeName(@NotNull String oldName, @NotNull String phone, @NotNull String newName) {
+ PhoneBookEntry entry = getEntry(oldName, phone);
+ if (entry == null) {
+ return Respond.NO_SUCH_ENTRY;
+ }
+
+ if (getEntry(newName, phone) != null) {
+ return Respond.ENTRY_ALREADY_EXISTS;
+ }
+
+ datastore.save(new PhoneBookEntry(entry.getId(), newName, entry.getPhone()));
+ return Respond.OK;
+ }
+
+ /**
+ * Changes phone number of a particular entry which determined by it's name and phone fields.
+ *
+ * @param name a person name.
+ * @param oldPhone old phone number.
+ * @param newPhone new phone number.
+ * @return NO_SUCH_ENTRY if there is no entry with given name and old phone in the database, ENTRY_ALREADY_EXISTS if
+ * there is an entry with given name and new phone in the database, OK otherwise.
+ */
+ public @NotNull Respond changePhone(@NotNull String name, @NotNull String oldPhone, @NotNull String newPhone) {
+ PhoneBookEntry entry = getEntry(name, oldPhone);
+ if (entry == null) {
+ return Respond.NO_SUCH_ENTRY;
+ }
+
+ if (getEntry(name, newPhone) != null) {
+ return Respond.ENTRY_ALREADY_EXISTS;
+ }
+
+ datastore.save(new PhoneBookEntry(entry.getId(), entry.getName(), newPhone));
+ return Respond.OK;
+ }
+
+ /**
+ * Returns a list of all entries in the database.
+ */
+ public @NotNull List getAllEntries() {
+ return datastore.createQuery(PhoneBookEntry.class).asList();
+ }
+
+ /**
+ * Drops the database.
+ */
+ public void drop() {
+ datastore.getDB().dropDatabase();
+ }
+
+ private @Nullable PhoneBookEntry getEntry(@NotNull String name, @NotNull String phone) {
+ List list = datastore.createQuery(PhoneBookEntry.class)
+ .field("name").equal(name)
+ .field("phone").equal(phone)
+ .asList();
+ if (list.isEmpty()) {
+ return null;
+ }
+
+ return list.get(0);
+ }
+
+ public enum Respond { OK, NO_SUCH_ENTRY, ENTRY_ALREADY_EXISTS };
+}
diff --git a/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/Main.java b/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/Main.java
new file mode 100644
index 0000000..1dfd1cb
--- /dev/null
+++ b/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/Main.java
@@ -0,0 +1,161 @@
+package ru.spbau.bachelor2015.veselov.hw10;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * Main class which implements interface of console application.
+ */
+public class Main {
+ private static final int promptCommand = 8;
+
+ private static final String prompt =
+ "0 - quit\n" +
+ "1 - add new entry\n" +
+ "2 - find phones by name\n" +
+ "3 - find names by phone\n" +
+ "4 - delete entry name-phone\n" +
+ "5 - change name of name-phone entry\n" +
+ "6 - change phone of name-phone entry\n" +
+ "7 - print all entries\n" +
+ "8 - print this prompt";
+
+ private static final String incorrectCommandMsg = "Incorrect command. Enter " + promptCommand + " to see prompt.";
+
+ /**
+ * Entry point of application.
+ *
+ * @param args no arguments required.
+ * @throws IOException any IOException which can occur during IO interaction with user.
+ */
+ public static void main(String[] args) throws IOException {
+ System.out.println("Welcome to console phone book!");
+ System.out.println("Please be careful, each token is read until end of line.");
+ System.out.println("Enter a command:");
+ System.out.println(prompt);
+
+ DataBase database = new DataBase("ru-spbau-bachelor2015-veselov-hw10");
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in))) {
+ mainLoop: while (true) {
+ String commandString = in.readLine();
+
+ int command;
+
+ try {
+ command = Integer.parseInt(commandString);
+ } catch (NumberFormatException e) {
+ System.out.println(incorrectCommandMsg);
+ continue;
+ }
+
+ String name;
+ String newName;
+ String phone;
+ String newPhone;
+ switch (command) {
+ case 0:
+ break mainLoop;
+
+ case 1:
+ System.out.println("Enter and to add new entry.");
+
+ name = in.readLine();
+ phone = in.readLine();
+ if (database.addNewEntry(name, phone) == DataBase.Respond.ENTRY_ALREADY_EXISTS) {
+ System.out.println("Entry with such name and phone already exists.");
+ }
+
+ break;
+
+ case 2:
+ System.out.println("Enter to find phones of this person.");
+
+ name = in.readLine();
+ for (PhoneBookEntry entry : database.findByName(name)) {
+ System.out.println(entry.getPhone());
+ }
+
+ break;
+
+ case 3:
+ System.out.println("Enter to find it's owners.");
+
+ phone = in.readLine();
+ for (PhoneBookEntry entry : database.findByPhone(phone)) {
+ System.out.println(entry.getName());
+ }
+
+ break;
+
+ case 4:
+ System.out.println("Enter and to delete entry.");
+
+ name = in.readLine();
+ phone = in.readLine();
+ if (database.deleteEntry(name, phone) == DataBase.Respond.NO_SUCH_ENTRY) {
+ System.out.println("Entry with such name and phone doesn't exist.");
+ }
+
+ break;
+
+ case 5:
+ System.out.println("Enter , and to change entry.");
+
+ name = in.readLine();
+ phone = in.readLine();
+ newName = in.readLine();
+ switch (database.changeName(name, phone, newName)) {
+ case NO_SUCH_ENTRY:
+ System.out.println("Entry with such name and phone doesn't exist.");
+ break;
+
+ case ENTRY_ALREADY_EXISTS:
+ System.out.println("Entry with given new name and phone already exists.");
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case 6:
+ System.out.println("Enter , and to change entry.");
+
+ name = in.readLine();
+ phone = in.readLine();
+ newPhone = in.readLine();
+ switch (database.changePhone(name, phone, newPhone)) {
+ case NO_SUCH_ENTRY:
+ System.out.println("Entry with such name and phone doesn't exist.");
+ break;
+
+ case ENTRY_ALREADY_EXISTS:
+ System.out.println("Entry with given name and new phone already exists.");
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case 7:
+ for (PhoneBookEntry entry : database.getAllEntries()) {
+ System.out.println("name: " + entry.getName() + ", phone: " + entry.getPhone());
+ }
+
+ break;
+
+ case 8:
+ System.out.println(prompt);
+ break;
+
+ default:
+ System.out.println(incorrectCommandMsg);
+ }
+ }
+ }
+ }
+}
diff --git a/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/PhoneBookEntry.java b/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/PhoneBookEntry.java
new file mode 100644
index 0000000..fce0c79
--- /dev/null
+++ b/10-DB/src/main/java/ru/spbau/bachelor2015/veselov/hw10/PhoneBookEntry.java
@@ -0,0 +1,76 @@
+package ru.spbau.bachelor2015.veselov.hw10;
+
+import org.bson.types.ObjectId;
+import org.jetbrains.annotations.NotNull;
+import org.mongodb.morphia.annotations.Entity;
+import org.mongodb.morphia.annotations.Id;
+
+/**
+ * Class which represents an entry in a phone book database. One entry consists of a name and a phone number.
+ * Format of a phone number isn't specified, so it's just a String field.
+ */
+@Entity
+public class PhoneBookEntry {
+ @Id
+ private final ObjectId id;
+
+ private final String name;
+ private final String phone;
+
+ /**
+ * Creates an empty object. Used by morphia.
+ */
+ public PhoneBookEntry() {
+ id = null;
+ name = null;
+ phone = null;
+ }
+
+ /**
+ * Creates a brand new entry which has a unique new id.
+ *
+ * @param name a person name.
+ * @param phone a phone number.
+ */
+ public PhoneBookEntry(@NotNull String name, @NotNull String phone) {
+ this.id = new ObjectId();
+
+ this.name = name;
+ this.phone = phone;
+ }
+
+ /**
+ * Creates an entry with a specified id. Must be used to reference existing entry in a database.
+ *
+ * @param id an id of an entry in a database.
+ * @param name a person name.
+ * @param phone a phone number.
+ */
+ public PhoneBookEntry(@NotNull ObjectId id, @NotNull String name, @NotNull String phone) {
+ this.id = id;
+
+ this.name = name;
+ this.phone = phone;
+ }
+
+ /**
+ * Returns id of an entry.
+ */
+ public @NotNull ObjectId getId() {
+ return id;
+ }
+
+ /**
+ * Returns a person name.
+ */
+ public @NotNull String getName() {
+ return name;
+ }
+
+ /**
+ * Returns a phone number.
+ */
+ public @NotNull String getPhone() {
+ return phone;
+ }
+}
diff --git a/10-DB/src/test/java/ru/spbau/bachelor2015/veselov/hw10/DataBaseTest.java b/10-DB/src/test/java/ru/spbau/bachelor2015/veselov/hw10/DataBaseTest.java
new file mode 100644
index 0000000..38ed686
--- /dev/null
+++ b/10-DB/src/test/java/ru/spbau/bachelor2015/veselov/hw10/DataBaseTest.java
@@ -0,0 +1,201 @@
+package ru.spbau.bachelor2015.veselov.hw10;
+
+import org.jetbrains.annotations.NotNull;
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class DataBaseTest {
+ private DataBase database = new DataBase("DataBase-test");
+
+ private final String name1 = "name1";
+
+ private final String name2 = "name2";
+
+ private final String name3 = "name3";
+
+ private final String phone1 = "phone1";
+
+ private final String phone2 = "phone2";
+
+ private final String phone3 = "phone3";
+
+ private final List allEntries = Arrays.asList(
+ new QuasiPhoneBookEntry(name1, phone1),
+ new QuasiPhoneBookEntry(name1, phone2),
+ new QuasiPhoneBookEntry(name2, phone1),
+ new QuasiPhoneBookEntry(name2, phone2));
+
+ private final List name1Entries = Arrays.asList(
+ new QuasiPhoneBookEntry(name1, phone1),
+ new QuasiPhoneBookEntry(name1, phone2));
+
+ private final List name2Entries = Arrays.asList(
+ new QuasiPhoneBookEntry(name2, phone1),
+ new QuasiPhoneBookEntry(name2, phone2));
+
+ private final List phone1Entries = Arrays.asList(
+ new QuasiPhoneBookEntry(name1, phone1),
+ new QuasiPhoneBookEntry(name2, phone1));
+
+ private final List phone2Entries = Arrays.asList(
+ new QuasiPhoneBookEntry(name1, phone2),
+ new QuasiPhoneBookEntry(name2, phone2));
+
+ private Comparator phoneBookEntryComparator =
+ (e1, e2) -> {
+ int nameComparisonResult = e1.getName().compareTo(e2.getName());
+ if (nameComparisonResult != 0) {
+ return nameComparisonResult;
+ }
+
+ return e1.getPhone().compareTo(e2.getPhone());
+ };
+
+ @After
+ public void tearDown() throws Exception {
+ database.drop();
+ }
+
+ @Test
+ public void testAddition() throws Exception {
+ fillDataBase();
+
+ assertEquals(DataBase.Respond.ENTRY_ALREADY_EXISTS, database.addNewEntry(name1, phone1));
+ assertEquals(DataBase.Respond.ENTRY_ALREADY_EXISTS, database.addNewEntry(name2, phone2));
+ assertEquals(DataBase.Respond.ENTRY_ALREADY_EXISTS, database.addNewEntry(name1, phone2));
+ assertEquals(DataBase.Respond.ENTRY_ALREADY_EXISTS, database.addNewEntry(name2, phone1));
+ }
+
+ @Test
+ public void testGetAllEntries() throws Exception {
+ assertQueryResult(database.getAllEntries(), Collections.emptyList());
+
+ fillDataBase();
+
+ assertQueryResult(database.getAllEntries(), allEntries);
+ }
+
+ @Test
+ public void testFindByName() throws Exception {
+ fillDataBase();
+
+ assertQueryResult(database.findByName(name1), name1Entries);
+ assertQueryResult(database.findByName(name2), name2Entries);
+ assertQueryResult(database.findByName(name3), Collections.emptyList());
+ }
+
+ @Test
+ public void testFindByPhone() throws Exception {
+ fillDataBase();
+
+ assertQueryResult(database.findByPhone(phone1), phone1Entries);
+ assertQueryResult(database.findByPhone(phone2), phone2Entries);
+ assertQueryResult(database.findByPhone(phone3), Collections.emptyList());
+ }
+
+ @Test
+ public void testDeletion() throws Exception {
+ fillDataBase();
+
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.deleteEntry(name3, phone1));
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.deleteEntry(name1, phone3));
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.deleteEntry(name3, phone3));
+
+ assertEquals(DataBase.Respond.OK, database.deleteEntry(name1, phone1));
+ assertEquals(DataBase.Respond.OK, database.deleteEntry(name1, phone2));
+ assertQueryResult(database.getAllEntries(), name2Entries);
+
+ assertEquals(DataBase.Respond.OK, database.addNewEntry(name1, phone1));
+ assertEquals(DataBase.Respond.OK, database.addNewEntry(name1, phone2));
+ assertQueryResult(database.getAllEntries(), allEntries);
+ }
+
+ @Test
+ public void testChangeName() throws Exception {
+ fillDataBase();
+
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.changeName(name3, phone1, name1));
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.changeName(name1, phone3, name1));
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.changeName(name3, phone3, name1));
+
+ assertEquals(DataBase.Respond.ENTRY_ALREADY_EXISTS, database.changeName(name1, phone1, name2));
+ assertEquals(DataBase.Respond.ENTRY_ALREADY_EXISTS, database.changeName(name2, phone2, name1));
+
+ assertEquals(DataBase.Respond.OK, database.changeName(name1, phone1, name3));
+ assertEquals(DataBase.Respond.OK, database.changeName(name1, phone2, name3));
+
+ assertQueryResult(database.getAllEntries(), Arrays.asList(
+ new QuasiPhoneBookEntry(name2, phone1),
+ new QuasiPhoneBookEntry(name2, phone2),
+ new QuasiPhoneBookEntry(name3, phone1),
+ new QuasiPhoneBookEntry(name3, phone2)));
+ }
+
+ @Test
+ public void testChangePhone() throws Exception {
+ fillDataBase();
+
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.changePhone(name1, phone3, phone1));
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.changePhone(name3, phone1, phone3));
+ assertEquals(DataBase.Respond.NO_SUCH_ENTRY, database.changePhone(name3, phone3, phone1));
+
+ assertEquals(DataBase.Respond.ENTRY_ALREADY_EXISTS, database.changePhone(name1, phone1, phone2));
+ assertEquals(DataBase.Respond.ENTRY_ALREADY_EXISTS, database.changePhone(name2, phone2, phone1));
+
+ assertEquals(DataBase.Respond.OK, database.changePhone(name1, phone1, phone3));
+ assertEquals(DataBase.Respond.OK, database.changePhone(name2, phone1, phone3));
+
+ assertQueryResult(database.getAllEntries(), Arrays.asList(
+ new QuasiPhoneBookEntry(name1, phone2),
+ new QuasiPhoneBookEntry(name1, phone3),
+ new QuasiPhoneBookEntry(name2, phone2),
+ new QuasiPhoneBookEntry(name2, phone3)));
+ }
+
+ private void assertQueryResult(@NotNull List result, @NotNull List expected) {
+ Collections.sort(result, phoneBookEntryComparator);
+ assertEquals(expected, result);
+ }
+
+ private void fillDataBase() {
+ for (QuasiPhoneBookEntry entry : allEntries) {
+ assertEquals(DataBase.Respond.OK, database.addNewEntry(entry.getName(), entry.getPhone()));
+ }
+ }
+
+ private static class QuasiPhoneBookEntry {
+ private final String name;
+
+ private final String phone;
+
+ public QuasiPhoneBookEntry(@NotNull String name, @NotNull String phone) {
+ this.name = name;
+ this.phone = phone;
+ }
+
+ public @NotNull String getName() {
+ return name;
+ }
+
+ public @NotNull String getPhone() {
+ return phone;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof PhoneBookEntry)) {
+ return false;
+ }
+
+ PhoneBookEntry other = (PhoneBookEntry) object;
+ return name.equals(other.getName()) && phone.equals(other.getPhone());
+ }
+ }
+}