diff --git a/Programming/ProgLab6/.gitignore b/Programming/ProgLab6/.gitignore
new file mode 100644
index 0000000..0bf5616
--- /dev/null
+++ b/Programming/ProgLab6/.gitignore
@@ -0,0 +1,48 @@
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Kotlin ###
+.kotlin
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
+
+save.json
+logs
\ No newline at end of file
diff --git a/Programming/ProgLab6/.idea/.gitignore b/Programming/ProgLab6/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/Programming/ProgLab6/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/Programming/ProgLab6/.idea/gradle.xml b/Programming/ProgLab6/.idea/gradle.xml
new file mode 100644
index 0000000..83fefe0
--- /dev/null
+++ b/Programming/ProgLab6/.idea/gradle.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Programming/ProgLab6/.idea/kotlinc.xml b/Programming/ProgLab6/.idea/kotlinc.xml
new file mode 100644
index 0000000..42dd882
--- /dev/null
+++ b/Programming/ProgLab6/.idea/kotlinc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Programming/ProgLab6/.idea/misc.xml b/Programming/ProgLab6/.idea/misc.xml
new file mode 100644
index 0000000..97740da
--- /dev/null
+++ b/Programming/ProgLab6/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Programming/ProgLab6/.idea/vcs.xml b/Programming/ProgLab6/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/Programming/ProgLab6/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Programming/ProgLab6/build.gradle b/Programming/ProgLab6/build.gradle
new file mode 100644
index 0000000..29ad40c
--- /dev/null
+++ b/Programming/ProgLab6/build.gradle
@@ -0,0 +1,33 @@
+plugins {
+ id 'org.jetbrains.kotlin.jvm' version '2.2.0'
+ id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.0'
+ id 'com.gradleup.shadow' version '9.3.1' apply false
+ id 'org.jetbrains.dokka' version '2.2.0'
+ }
+
+group = 'com.leterzp.prog'
+version = '2.0'
+
+repositories {
+ mavenCentral()
+}
+
+subprojects {
+ apply plugin: 'org.jetbrains.kotlin.jvm'
+ apply plugin: 'org.jetbrains.kotlin.plugin.serialization'
+ apply plugin: 'org.jetbrains.dokka'
+
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ testImplementation 'org.jetbrains.kotlin:kotlin-test'
+ implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
+ }
+
+ kotlin {
+ jvmToolchain(17)
+ }
+}
+
diff --git a/Programming/ProgLab6/client/build.gradle b/Programming/ProgLab6/client/build.gradle
new file mode 100644
index 0000000..dbf5b77
--- /dev/null
+++ b/Programming/ProgLab6/client/build.gradle
@@ -0,0 +1,16 @@
+group = 'com.leterzp.prog.client'
+version = '2.0'
+
+dependencies {
+ implementation project(":general")
+}
+
+apply plugin: 'com.gradleup.shadow'
+
+shadowJar {
+ archiveBaseName.set('ProgLab6.client')
+ archiveVersion.set('2.0')
+ manifest {
+ attributes 'Main-Class': 'ClientKt'
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/Client.kt b/Programming/ProgLab6/client/src/main/kotlin/Client.kt
new file mode 100644
index 0000000..5c952f0
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/Client.kt
@@ -0,0 +1,19 @@
+import core.ConnectionManager
+import core.InteractiveMode
+import exceptions.ProgramExitException
+import io.IOManager
+import java.net.InetAddress
+
+fun main() {
+ val host = InetAddress.getLocalHost()
+ val port = 8841
+ val io = IOManager()
+ val cm = ConnectionManager(io, host, port)
+ try {
+ val im = InteractiveMode(io, cm)
+ im.start()
+ } catch (e: ProgramExitException) {
+ io.write(e.message+"\n")
+ }
+
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/commands/ExecuteScriptCommand.kt b/Programming/ProgLab6/client/src/main/kotlin/commands/ExecuteScriptCommand.kt
new file mode 100644
index 0000000..80f8cd8
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/commands/ExecuteScriptCommand.kt
@@ -0,0 +1,41 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для исполнения скрипта.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class ExecuteScriptCommand(override val ci: CommandInvoker): Command(ci) {
+ override val argumentsAmount: Int = 1
+
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ if (arguments[0] !in ci.executionHistory) {
+ ci.executionHistory.add(arguments[0])
+ val previousSource = ci.io.source
+ ci.io.source = arguments[0]
+ ci.addNext(ci.io.read())
+ ci.io.source = previousSource
+ } else {
+ result = "Обнаружена бесконечная рекурсия. Отказ в запуске скрипта ${arguments[0]}.\n"
+ }
+ }
+
+ override fun describe(): String {
+ return "Запускает команды из скрипта"
+ }
+
+ override fun getSyntax(): String {
+ return "[file_name]"
+ }
+
+ override fun getName(): String {
+ return "execute_script"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/commands/HelpCommand.kt b/Programming/ProgLab6/client/src/main/kotlin/commands/HelpCommand.kt
new file mode 100644
index 0000000..e991396
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/commands/HelpCommand.kt
@@ -0,0 +1,73 @@
+package commands
+
+import core.CommandInvoker
+import exceptions.CommandNotFoundException
+import exceptions.InvalidAmountOfArgumentsException
+
+/**
+ * Команда для вывода списка доступных команд.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class HelpCommand(override val ci: CommandInvoker): Command(ci) {
+ /**
+ * Выдаёт полную информацию о команде.
+ *
+ * @param command Команда типа [Command].
+ *
+ * @return Информацию о команде типа [String].
+ *
+ * @since 1.0
+ */
+ private fun getFullInfo(command: CommandWrapper): String {
+ return " --" + command.string + " : " + command.describe
+ }
+
+ /**
+ * Выдаёт краткую информацию о команде.
+ *
+ * @param command Команда типа [Command].
+ *
+ * @return Информацию о команде типа [String].
+ *
+ * @since 1.0
+ */
+ private fun getInfo(command: CommandWrapper): String {
+ return " --" + command.name + " : " + command.describe
+ }
+
+ override fun execute(arguments: List) {
+ try {
+ if (arguments.isEmpty()) {
+ result = "Список доступных команд:" + "\n"
+ for (command in ci.commands.values) {
+ result += getInfo(command) + "\n"
+ }
+ } else if (arguments.size == 1) {
+ if (arguments[0] in ci.commands.keys) {
+ result += getFullInfo(ci.commands.get(arguments[0])!!) + "\n"
+ } else {
+ throw CommandNotFoundException(arguments[0])
+ }
+ } else throw InvalidAmountOfArgumentsException(this, arguments.size)
+ } catch (e: CommandNotFoundException) {
+ result = e.message + "\n"
+ }
+ }
+
+ override fun getName(): String {
+ return "help"
+ }
+
+ override fun getSyntax(): String {
+ return "[ | command]"
+ }
+
+ override fun describe(): String {
+ return "Выводит информацию о всех командах либо описание одной команды"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/core/CommandInvoker.kt b/Programming/ProgLab6/client/src/main/kotlin/core/CommandInvoker.kt
new file mode 100644
index 0000000..408a071
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/core/CommandInvoker.kt
@@ -0,0 +1,208 @@
+package core
+
+import commands.*
+import elements.CityBuilder
+import exceptions.CommandNotFoundException
+import exceptions.ConnectionException
+import exceptions.InvalidElementValueException
+import exceptions.NoNextCommandException
+import exceptions.ProgramExitException
+import io.IOManager
+import java.util.Stack
+
+/**
+ * Класс вызова команд.
+ *
+ * Позволяет вызывать инициализированные в нём команды для работы с коллекцией посредством [CollectionManager].
+ *
+ * @param io [IOManager], откуда читаются команды.
+ *
+ * @property commands [HashMap] команд, содержащий имя команды типа [String] и саму команду типа [Command].
+ *
+ * @constructor Принимает все описанные выше параметры, создавая объект, уже содержащий в себе стандартный набор команд.
+ *
+ * @since 1.0
+ */
+class CommandInvoker(val io: IOManager, private val cm: ConnectionManager): CommandInvokerInterface {
+ val commands: HashMap = HashMap()
+ private val nextArgument: Stack = Stack()
+ val executionHistory = Stack()
+
+ init {
+ try {
+ getCommands()
+ } catch (e: ConnectionException) {
+ io.write(e.message + "\n")
+ throw ProgramExitException()
+ }
+ }
+
+ /**
+ * Инициализирует команду, добавляя её в список возможных к использованию.
+ *
+ * @param command Команда для инициализации, типа [Command].
+ *
+ * @since 1.0
+ */
+ fun initializeCommand(command: CommandWrapper) {
+ val name: String = command.name
+ commands[name] = command
+ }
+
+ /**
+ * Вызывает команду, читая её либо из очереди команд, либо из консоли в случае, если очередь пуста.
+ *
+ * @since 1.0
+ */
+ override fun executeCommand(cw: CommandWrapper): CommandWrapper {
+ when (cw.name) {
+ "help" -> {
+ val help = HelpCommand(this)
+ help.execute(cw.arguments)
+ cw.result = help.result
+ return cw
+ }
+ "execute_script" -> {
+ val script = ExecuteScriptCommand(this)
+ script.execute(cw.arguments)
+ cw.result = script.result
+ return cw
+ }
+ "exit" -> {
+ val exit = ExitCommand(this)
+ exit.execute(cw.arguments)
+ cw.result = exit.result
+ return cw
+ }
+ else -> {
+ getCommands()
+ val result = cm.sendAndReceive(cw)
+ return result
+ }
+ }
+ }
+
+ fun readCommand() {
+ var instruction: List = try {
+ readNext().trim().split(" ")
+ } catch (_: NoNextCommandException) {
+ io.read().trim().split(" ")
+ }
+ if (instruction.size == 1 && instruction[0] == "") return
+ try {
+ if (instruction[0] !in run {
+ val list1 = mutableListOf()
+ for (i in this@CommandInvoker.getStandardCommands()) {
+ list1.add(i.name)
+ }
+ list1
+ }) {
+ // getCommands()
+ if (instruction[0] !in commands.keys) throw CommandNotFoundException(instruction[0])
+ }
+ val command = commands[instruction[0]]!!
+ instruction = validateCommand(command, instruction)
+ command.arguments = instruction.minus(instruction[0])
+ io.write(executeCommand(command).result)
+ } catch (e: CommandNotFoundException) {
+ io.write(e.message + "\n")
+ } catch (e: InvalidElementValueException) {
+ io.write(e.message + "\n")
+ }
+ }
+
+ /**
+ * Читает первую в очереди команду.
+ *
+ * @return Команду с аргументами типа [String].
+ *
+ * @throws NoNextCommandException В случае, если очередь из команд пуста.
+ *
+ * @since 1.0
+ */
+ fun readNext(): String {
+ val result: String
+ if (nextArgument.isEmpty()) {
+ executionHistory.clear()
+ throw NoNextCommandException()
+ }
+ else {
+ result = nextArgument.pop()
+ }
+ return result
+ }
+
+ /**
+ * Добавляет одну или несколько команд в очередь.
+ *
+ * @param instructions Одна или несколько команд с аргументами типа [String].
+ *
+ * @since 1.0
+ */
+ fun addNext(instructions: String) {
+ val values: List = instructions.lines().reversed()
+ for (instruction in values) {
+ nextArgument.push(instruction)
+ }
+ }
+
+ /**
+ * Выбирает следующее значение для ввода.
+ *
+ * @see [IOManager.askForValue].
+ */
+ fun nextValue(output: String): String {
+ val input: String
+ if (nextArgument.isEmpty()) {
+ input = io.askForValue(output)
+ } else {
+ input = nextArgument.pop()
+ }
+ return input
+ }
+
+ fun getCommands() {
+ val list = cm.askCommandList()
+ commands.clear()
+ for (command in list) {
+ initializeCommand(command)
+ }
+ for (st in getStandardCommands()) {
+ initializeCommand(st)
+ }
+ }
+
+ fun validateCommand(command: CommandWrapper, instruction: List): List {
+ if (command.argumentsAmount > 1) {
+ val args = mutableListOf()
+ args.add(instruction[0])
+ val creator = CityBuilder()
+ var count: Int = 0
+ while (true) {
+ val value: String = nextValue(creator.getField(count))
+ try {
+ creator.setField(value, count)
+ args.add(value)
+ } catch (e: InvalidElementValueException) {
+ io.write(e.message + "\n")
+ continue
+ }
+ if (count == creator.size-1) break
+ count++
+ }
+ return args.toList()
+ } else if (!commands[instruction[0]]!!.validate(instruction.minus(instruction[0])))
+ throw InvalidElementValueException(instruction.minus(instruction[0]))
+ return instruction
+ }
+
+ fun getStandardCommands(): List {
+ val help = CommandWrapper()
+ help.wrapCommand(HelpCommand(this))
+ val exit = CommandWrapper()
+ exit.wrapCommand(ExitCommand(this))
+ val script = CommandWrapper()
+ script.wrapCommand(ExecuteScriptCommand(this))
+ return listOf(help, exit, script)
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/core/ConnectionManager.kt b/Programming/ProgLab6/client/src/main/kotlin/core/ConnectionManager.kt
new file mode 100644
index 0000000..f3656cf
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/core/ConnectionManager.kt
@@ -0,0 +1,56 @@
+package core
+
+import commands.CommandWrapper
+import exceptions.ConnectionException
+import io.IOManager
+import java.net.InetSocketAddress
+import java.net.InetAddress
+import java.net.SocketAddress
+import java.nio.ByteBuffer
+import java.nio.channels.DatagramChannel
+import java.nio.channels.SelectionKey
+import java.nio.channels.Selector
+import kotlinx.serialization.json.Json.Default.encodeToString
+import kotlinx.serialization.json.Json.Default.decodeFromString
+
+class ConnectionManager(private val io: IOManager, private val host: InetAddress, private val port: Int) {
+ fun askCommandList(): List {
+ val cw = CommandWrapper()
+ cw.name = "help"
+ val result = sendAndReceive(cw)
+ return decodeFromString(result.result)
+ }
+
+ fun sendAndReceive(cw: CommandWrapper): CommandWrapper {
+ val address: SocketAddress = InetSocketAddress(host, port)
+ val selector = Selector.open()
+ val channel = DatagramChannel.open()
+ val sendBuffer = ByteBuffer.wrap(encodeToString(cw).toByteArray())
+ val bytes = ByteArray(32768)
+ val receiveBuffer = ByteBuffer.wrap(bytes)
+ var result = CommandWrapper()
+ channel.configureBlocking(false)
+ val key = channel.register(selector, SelectionKey.OP_READ)
+ for (i in 1..3) {
+ sendBuffer.position(0)
+ channel.send(sendBuffer, address)
+ try {
+ selector.select(10000)
+ if (key.isReadable) {
+ channel.receive(receiveBuffer)
+ result = decodeFromString(bytes.decodeToString().replace("\u0000", ""))
+ break
+ } else {
+ throw ConnectionException()
+ }
+ } catch (_: ConnectionException) {
+ if (i == 3) {
+ throw ConnectionException()
+ }
+ io.write("Не удалось установить соединение с сервером. Попытка ${i+1} из 3.\n")
+ continue
+ }
+ }
+ return result
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/core/InteractiveMode.kt b/Programming/ProgLab6/client/src/main/kotlin/core/InteractiveMode.kt
new file mode 100644
index 0000000..956de23
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/core/InteractiveMode.kt
@@ -0,0 +1,56 @@
+package core
+
+import exceptions.*
+import io.IOManager
+import java.io.IOException
+
+/**
+ * Класс интерактивного взаимодействия с программой.
+ *
+ * Позволяет консольно взаимодействовать с программой и вводить команды через консоль..
+ *
+ * @constructor Принимает все описанные выше параметры.
+ *
+ * @since 1.0
+ */
+class InteractiveMode(private val io: IOManager, private val cm: ConnectionManager) {
+ private val ci: CommandInvoker = CommandInvoker(io, cm)
+
+ /**
+ * Запускает взаимодействие с программой.
+ *
+ * @since 1.0
+ */
+ private fun interaction() {
+ var isWorking = true
+ ci.getCommands()
+ while (isWorking) {
+ try {
+ io.write("=> ")
+ ci.readCommand()
+ } catch (e: ProgramExitException) {
+ io.write(e.message + "\n")
+ isWorking = false
+ } catch (e: IOException) {
+ io.source = null
+ io.write("Ошибка чтения файла или записи в него.\n")
+ } catch (e: ConnectionException) {
+ io.write(e.message + "\n")
+ } catch (e: Exception) {
+ io.write("Возникла непредвиденная ошибка: " + e.message + "\n")
+ io.write("Экстренное завершение работы.\n")
+ isWorking = false
+ }
+ }
+ }
+
+ /**
+ * Запускает интерактивный режим.
+ *
+ * @since 1.0
+ */
+ fun start() {
+ io.write("Программа запущена в интерактивном режиме. Чтобы увидеть список команд, введите help.\n")
+ interaction()
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/io/FileIO.kt b/Programming/ProgLab6/client/src/main/kotlin/io/FileIO.kt
new file mode 100644
index 0000000..1d746e4
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/io/FileIO.kt
@@ -0,0 +1,50 @@
+package io
+
+import java.io.BufferedInputStream
+import java.io.BufferedWriter
+import java.io.FileInputStream
+import java.io.FileWriter
+
+/**
+ * Класс для чтения данных из файла.
+ *
+ * @param file Имя файла типа [String].
+ *
+ * @constructor Создаёт готовый к использованию объект, принимая все описанные выше параметры.
+ *
+ * @since 1.0
+ */
+class FileIO(private val file: String) {
+
+ /**
+ * Читает весь файл.
+ *
+ * @return [String] строку файла.
+ *
+ * @throws [NoSuchElementException] В случае, если в файле не осталось непрочитанных строк.
+ * @throws [java.io.IOException] В случае, если файла не существует.
+ *
+ * @since 1.0
+ */
+ fun readFile(): String {
+ val reader = BufferedInputStream(FileInputStream(file))
+ val text = reader.readAllBytes().decodeToString()
+ reader.close()
+ return text
+ }
+
+ /**
+ * Записывает строки в файл.
+ *
+ * @param text Строки для записи типа [String].
+ *
+ * @throws [java.io.IOException] В случае, если файла не существует.
+ *
+ * @since 1.0
+ */
+ fun writeToFile(text: String) {
+ val writer = BufferedWriter(FileWriter(file))
+ writer.write(text)
+ writer.close()
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/io/IOManager.kt b/Programming/ProgLab6/client/src/main/kotlin/io/IOManager.kt
new file mode 100644
index 0000000..afbaac7
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/io/IOManager.kt
@@ -0,0 +1,83 @@
+package io
+
+import java.io.IOException
+
+/**
+ * Класс для управления чтения и записи из файлов и консоли.
+ *
+ * @property source Путь к файлу, из которого читаются данные типа [String], по умолчанию null(консоль).
+ *
+ * @constructor Создаёт объект для взаимодействия с консолью.
+ *
+ * @since 1.0
+ */
+class IOManager() {
+ var source: String? = null
+ set(value) {
+ sourceType = if (value == "" || value == null) {
+ IOType.CONSOLE
+ } else {
+ IOType.FILE
+ }
+ if (sourceType == IOType.FILE) { io = FileIO(value!!) }
+ field = value
+ }
+ private var sourceType: IOType = IOType.CONSOLE
+ private lateinit var io: FileIO
+
+ /**
+ * Читает данные из [source].
+ *
+ * @return Строка из [source] типа [String].
+ *
+ * @throws [IOException] В случае, если данные не могут быть корректно прочитаны.
+ *
+ * @since 1.0
+ */
+ fun read(): String {
+ val input: String = when (sourceType) {
+ IOType.CONSOLE -> readln()
+ IOType.FILE -> {
+ io.readFile()
+ }
+ }
+ return input
+ }
+
+ /**
+ * Записывает данные в [source].
+ *
+ * @param output Данные для записи типа [String].
+ *
+ * @throws [IOException] В случае, если данные не могут быть корректно записаны.
+ *
+ * @since 1.0
+ */
+ fun write(output: String) {
+ when (sourceType) {
+ IOType.CONSOLE -> print(output)
+ IOType.FILE -> {
+ val io: FileIO = FileIO(source!!)
+ io.writeToFile(output)
+ }
+ }
+ }
+
+ /**
+ * Запрашивает ввод значения.
+ *
+ * @param value Строковое описание необходимого значения типа [String].
+ *
+ * @return Значение типа [String].
+ *
+ * @throws [IOException] В случае, если возникли проблемы с чтением/записью данных.
+ *
+ * @since 1.0
+ */
+ fun askForValue(value: String): String {
+ val output = "Введите $value: "
+ write(output)
+ val text: String = read()
+ return text
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/client/src/main/kotlin/io/IOType.kt b/Programming/ProgLab6/client/src/main/kotlin/io/IOType.kt
new file mode 100644
index 0000000..d497c78
--- /dev/null
+++ b/Programming/ProgLab6/client/src/main/kotlin/io/IOType.kt
@@ -0,0 +1,6 @@
+package io
+
+/**
+ * Типы возможных способов чтения данных, используемых программой.
+ */
+enum class IOType {FILE, CONSOLE}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/build.gradle b/Programming/ProgLab6/general/build.gradle
new file mode 100644
index 0000000..a196c81
--- /dev/null
+++ b/Programming/ProgLab6/general/build.gradle
@@ -0,0 +1,2 @@
+group = 'com.leterzp.prog.general'
+version = '2.0'
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/commands/Command.kt b/Programming/ProgLab6/general/src/main/kotlin/commands/Command.kt
new file mode 100644
index 0000000..a6af759
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/commands/Command.kt
@@ -0,0 +1,76 @@
+package commands
+
+import core.CommandInvokerInterface
+import exceptions.InvalidAmountOfArgumentsException
+import kotlinx.serialization.Serializable
+
+/**
+ * Абстрактный класс для всех команд.
+ *
+ * @property argumentsAmount Количество аргументов, которые принимает команда, типа [Int].
+ *
+ * @since 1.0
+ */
+abstract class Command(protected open val ci: CommandInvokerInterface) {
+ open val argumentsAmount: Int = 0
+ open var result: String = ""
+
+ override fun toString(): String {
+ if (getSyntax() == "") return getName() + getSyntax()
+ return getName() + " " + getSyntax()
+ }
+
+ /**
+ * Исполняет команду.
+ *
+ * @param arguments [List], содержащий в себе все аргументы команды типа [String].
+ *
+ * @throws InvalidAmountOfArgumentsException В случае, если количество данных элементов не совпадает с
+ * количеством элементов, которые принимает команда.
+ *
+ * @since 1.0
+ */
+ open fun execute(arguments: List) {
+ result = ""
+ var arguments = arguments
+ while (arguments.contains("")) arguments = arguments.minus("")
+ if (arguments.size != argumentsAmount) throw InvalidAmountOfArgumentsException(this, arguments.size)
+ }
+
+ /**
+ * Описывает команду.
+ *
+ * @return Описание команды типа [String].
+ *
+ * @since 1.0
+ */
+ open fun describe(): String {
+ return "У этой команды нет описания"
+ }
+
+ /**
+ * Описывает синтаксис команды.
+ *
+ * @return Синтаксис команды типа [String].
+ *
+ * @since 1.0
+ */
+ open fun getSyntax(): String {
+ return ""
+ }
+
+ /**
+ * Описывает имя команды.
+ *
+ * @return Имя команды типа [String].
+ *
+ * @since 1.0
+ */
+ open fun getName(): String {
+ return "command"
+ }
+
+ open fun validate(arguments: List, count: Int = 0): Boolean {
+ return true
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/commands/CommandWrapper.kt b/Programming/ProgLab6/general/src/main/kotlin/commands/CommandWrapper.kt
new file mode 100644
index 0000000..90a8165
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/commands/CommandWrapper.kt
@@ -0,0 +1,37 @@
+package commands
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+class CommandWrapper() {
+ var command: String = ""
+ var argumentsAmount = 0
+ var arguments: List = listOf("")
+ var string: String = ""
+ var describe: String = ""
+ var syntax: String = ""
+ var name: String = ""
+ var result: String = ""
+
+ fun wrapCommand(c: Command) {
+ command = c.getName()
+ argumentsAmount = c.argumentsAmount
+ string = c.toString()
+ describe = c.describe()
+ syntax = c.getSyntax()
+ name = c.getName()
+ }
+
+ fun validate(args: List): Boolean {
+ if (args.size == argumentsAmount) return true
+ if (args.size == 1) {
+ try {
+ args[0].toLong()
+ return true
+ } catch (_: NumberFormatException) {
+ return false
+ }
+ }
+ return false
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/commands/ExitCommand.kt b/Programming/ProgLab6/general/src/main/kotlin/commands/ExitCommand.kt
new file mode 100644
index 0000000..de27ea2
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/commands/ExitCommand.kt
@@ -0,0 +1,28 @@
+package commands
+
+import core.CommandInvokerInterface
+import exceptions.ProgramExitException
+
+/**
+ * Команда для выхода из программы.
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @throws ProgramExitException В качестве сигнала завершения программы.
+ *
+ * @since 1.0
+ */
+class ExitCommand(ci: CommandInvokerInterface): Command(ci) {
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ throw ProgramExitException()
+ }
+
+ override fun describe(): String {
+ return "Завершает работу программы"
+ }
+
+ override fun getName(): String {
+ return "exit"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/core/CommandInvokerInterface.kt b/Programming/ProgLab6/general/src/main/kotlin/core/CommandInvokerInterface.kt
new file mode 100644
index 0000000..5e8008b
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/core/CommandInvokerInterface.kt
@@ -0,0 +1,7 @@
+package core
+
+import commands.CommandWrapper
+
+interface CommandInvokerInterface {
+ fun executeCommand(cw: CommandWrapper): CommandWrapper
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/elements/City.kt b/Programming/ProgLab6/general/src/main/kotlin/elements/City.kt
new file mode 100644
index 0000000..adf4c3f
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/elements/City.kt
@@ -0,0 +1,105 @@
+package elements
+
+import exceptions.InvalidElementValueException
+import java.time.LocalDate
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.Contextual
+
+/**
+ * Город.
+ *
+ * Элемент коллекции.
+ *
+ * @param name Название города типа [String].
+ * @param coordinates Координаты города типа [Coordinates].
+ * @param area Площадь города типа положительный [Double].
+ * @param population Население города типа положительный [Int].
+ * @param metersAboveSeaLevel Высота над уровнем моря типа [Long].
+ * @param populationDensity Плотность населения типа положительный [Float].
+ * @param governon Губернатор города типа [Human].
+ * @param climate Климат города типа [Climate], может быть null.
+ * @param government Правительство типа [Government], может быть null.
+ *
+ * @property id Уникальный номер города типа положительный [Long].
+ * @property creationDate Дата создания города типа [LocalDate].
+ *
+ * @constructor Принимает все параметры, описанные выше, создавая полноценный объект.
+ *
+ * @since 1.0
+ */
+@Serializable
+class City(
+ val name1: String,
+ var coordinates: Coordinates,
+ val area1: Double,
+ val population1: Int,
+ var metersAboveSeaLevel: Long,
+ val populationDensity1: Float,
+ var governon: Human,
+ var climate: Climate? = null,
+ var government: Government? = null
+): Comparable {
+
+ var name: String = name1
+ set(value) {
+ if (value == "") throw InvalidElementValueException(value)
+ field = value
+ }
+ var area: Double = area1
+ set(value) {
+ if (value <= 0) throw InvalidElementValueException(value)
+ field = value
+ }
+ var population: Int = population1
+ set(value) {
+ if (value <= 0) throw InvalidElementValueException(value)
+ field = value
+ }
+ var populationDensity: Float = populationDensity1
+ set(value) {
+ if (value <= 0) throw InvalidElementValueException(value)
+ field = value
+ }
+ val id: Long = counter
+ @Contextual
+ val creationDate: LocalDate = LocalDate.now()
+
+ companion object { private var counter: Long = 1 }
+
+ init{
+ if (id >= counter) {
+ counter = id + 1
+ }
+ }
+
+ override fun compareTo(other: City): Int {
+ return this.id.compareTo(other.id)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other == null) return false
+ if (other is City) {
+ if (this.id == other.id) return true
+ }
+ return false
+ }
+
+ override fun toString(): String {
+ var output: String = """
+ --$id: Город $name
+ был создан $creationDate
+ расположен по координатам $coordinates
+ $metersAboveSeaLevel метров над уровнем моря
+ занимает площадь $area
+ с населением $population и его плотностью $populationDensity
+ управляет им $governon
+ """.trimIndent()
+ if (government != null) {
+ output += "\n с правительством $government"
+ }
+ if (climate != null) {
+ output += "\n да и погода там $climate"
+ }
+ return output
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/elements/CityBuilder.kt b/Programming/ProgLab6/general/src/main/kotlin/elements/CityBuilder.kt
new file mode 100644
index 0000000..0f71627
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/elements/CityBuilder.kt
@@ -0,0 +1,187 @@
+package elements
+
+import exceptions.InvalidElementValueException
+
+/**
+ * Builder для класса [City].
+ *
+ * Может поэтапно создавать и изменять объекты класса [City].
+ *
+ * @property size Количество свойств типа [Int], необходимых для создания [City].
+ *
+ * @since 1.0
+ */
+class CityBuilder {
+ private var name: String? = null
+ set(value) {
+ if (value == null) throw InvalidElementValueException("")
+ field = value
+ }
+ private var coordinateX: Int? = null
+ set(value) {
+ if (value is Int && value > -827) field = value
+ else throw InvalidElementValueException(value?: "")
+ }
+ private var coordinateY: Double? = null
+ set(value) {
+ if (value == null) throw InvalidElementValueException("")
+ field = value
+ }
+ private var area: Double? = null
+ set(value) {
+ if (value is Double && value > 0) field = value
+ else throw InvalidElementValueException(value?: "")
+ }
+ private var population: Int? = null
+ set(value) {
+ if (value is Int && value > 0) field = value
+ else throw InvalidElementValueException(value?: "")
+ }
+ private var metersAboveSeaLevel: Long? = null
+ set(value) {
+ if (value == null) throw InvalidElementValueException("")
+ field = value
+ }
+ private var populationDensity: Float? = null
+ set(value) {
+ if (value is Float && value > 0) field = value
+ else throw InvalidElementValueException(value?: "")
+ }
+ private var govName: String? = null
+ set(value) {
+ if (value == null) throw InvalidElementValueException("")
+ field = value
+ }
+ private var govAge: Long? = null
+ set(value) {
+ if (value is Long && value > 0) field = value
+ else throw InvalidElementValueException(value?: "")
+ }
+ private var govHeight: Float? = null
+ set(value) {
+ if (value is Float && value > 0) field = value
+ else throw InvalidElementValueException(value?: "")
+ }
+ private var climate: Climate? = null
+ private var government: Government? = null
+ private val fields: Array = arrayOf(
+ "название города (String)",
+ "координата X (Int)",
+ "координата Y (Double)",
+ "площадь (Double)",
+ "население (Int)",
+ "высоту над уровнем моря (Long)",
+ "плотность населения (Float)",
+ "имя губернатора (String)",
+ "возраст губернатора (Long)",
+ "рост губернатора (Float)",
+ "климат (RAIN_FOREST | MONSOON | HUMIDCONTINENTAL | SUBARCTIC | TUNDRA)",
+ "правительство (ARISTOCRACY | ANARCHY | KLEPTOCRACY | CORPORATOCRACY | JUNTA)"
+ )
+ val size: Int = 12
+
+ /**
+ * Используется для получения названия и типа свойства класса.
+ *
+ * @param count Номер свойства типа [Int].
+ *
+ * @return Имя и тип свойства типа [String].
+ *
+ * @since 1.0
+ */
+ fun getField(count: Int): String {
+ return fields[count]
+ }
+
+ /**
+ * Используется для установки значения свойства.
+ *
+ * @param input Новое значение свойства типа [String].
+ * @param count Номер свойства типа [Int].
+ *
+ * @throws InvalidElementValueException В случае, если значение не может быть установлено.
+ *
+ * @since 1.0
+ */
+ fun setField(input: String, count: Int) {
+ val value: String? = if (input == "") null else input
+ try {
+ when (count) {
+ 0 -> name = value
+ 1 -> coordinateX = value?.toInt()
+ 2 -> coordinateY = value?.toDouble()
+ 3 -> area = value?.toDouble()
+ 4 -> population = value?.toInt()
+ 5 -> metersAboveSeaLevel = value?.toLong()
+ 6 -> populationDensity = value?.toFloat()
+ 7 -> govName = value
+ 8 -> govAge = value?.toLong()
+ 9 -> govHeight = value?.toFloat()
+ 10 -> climate = run {
+ val result: Climate? = if (value != null) Climate.valueOf(value.uppercase()) else null
+ result
+ }
+
+ 11 -> government = run {
+ val result: Government? = if (value != null) Government.valueOf(value.uppercase()) else null
+ result
+ }
+ }
+ } catch (_: NumberFormatException) {
+ throw InvalidElementValueException(value?:"")
+ } catch (_: IllegalArgumentException) {
+ throw InvalidElementValueException(value?:"")
+ }
+ }
+
+ /**
+ * Создаёт объект типа [City] на основе загруженных полей.
+ *
+ * @return Город типа [City].
+ *
+ * @throws InvalidElementValueException В случае, если какое-либо из установленных значений не подходит под
+ * требования класса [City].
+ *
+ * @since 1.0
+ */
+ fun create(): City {
+ if (name != null
+ && coordinateX != null
+ && coordinateY != null
+ && area != null
+ && population != null
+ && metersAboveSeaLevel != null
+ && populationDensity != null
+ && govName != null
+ && govAge != null
+ && govHeight != null
+ ) {
+ val cords = Coordinates(coordinateX!!, coordinateY!!)
+ val governon = Human(govName!!, govAge!!, govHeight!!)
+ return City(name!!, cords, area!!,
+ population!!, metersAboveSeaLevel!!, populationDensity!!,
+ governon, climate, government)
+ } else throw InvalidElementValueException("City")
+ }
+
+ /**
+ * Изменяет свойства уже имеющегося объекта типа [City].
+ *
+ * @param city Город для изменения типа [City].
+ *
+ * @since 1.0
+ */
+ fun update(city: City) {
+ if (name != null) city.name = name!!
+ if (coordinateX != null && coordinateY != null)
+ city.coordinates = Coordinates(coordinateX!!, coordinateY!!)
+ if (area != null) city.area = area!!
+ if (population != null) city.population = population!!
+ if (metersAboveSeaLevel != null) city.metersAboveSeaLevel = metersAboveSeaLevel!!
+ if (populationDensity != null) city.populationDensity = populationDensity!!
+ if (govName != null && govAge != null && govHeight != null)
+ city.governon = Human(govName!!, govAge!!, govHeight!!)
+ if (climate != null) city.climate = climate
+ if (government != null) city.government = government
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/elements/Climate.kt b/Programming/ProgLab6/general/src/main/kotlin/elements/Climate.kt
new file mode 100644
index 0000000..d2ec6ad
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/elements/Climate.kt
@@ -0,0 +1,6 @@
+package elements
+
+/**
+ * Перечисление возможных климатов для города [City].
+ */
+enum class Climate {RAIN_FOREST, MONSOON, HUMIDCONTINENTAL, SUBARCTIC, TUNDRA}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/elements/Coordinates.kt b/Programming/ProgLab6/general/src/main/kotlin/elements/Coordinates.kt
new file mode 100644
index 0000000..23aeb45
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/elements/Coordinates.kt
@@ -0,0 +1,28 @@
+package elements
+
+import exceptions.InvalidElementValueException
+import kotlinx.serialization.Serializable
+
+/**
+ * Координаты для города [City].
+ *
+ * @param x Координата по оси X типа [Int] больше -827.
+ * @param y Координата по оси Y типа [Double].
+ *
+ * @throws InvalidElementValueException В случае, если координаты не соответствуют необходимым требованиям.
+ *
+ * @constructor Принимает все указанные выше параметры, создавая готовый к использованию
+ * объект с заданными координатами.
+ *
+ * @since 1.0
+ */
+@Serializable
+class Coordinates(private val x: Int, private val y: Double) {
+ init{
+ if(x <= -827) throw InvalidElementValueException(x)
+ }
+
+ override fun toString(): String {
+ return "x = $x, y = $y"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/elements/Government.kt b/Programming/ProgLab6/general/src/main/kotlin/elements/Government.kt
new file mode 100644
index 0000000..59d7021
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/elements/Government.kt
@@ -0,0 +1,6 @@
+package elements
+
+/**
+ * Перечисления возможных видов правительства для города [City].
+ */
+enum class Government{ ARISTOCRACY, ANARCHY, KLEPTOCRACY, CORPORATOCRACY, JUNTA}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/elements/Human.kt b/Programming/ProgLab6/general/src/main/kotlin/elements/Human.kt
new file mode 100644
index 0000000..c87e79b
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/elements/Human.kt
@@ -0,0 +1,24 @@
+package elements
+
+import exceptions.InvalidElementValueException
+import kotlinx.serialization.Serializable
+
+/**
+ * Человек для города [City].
+ *
+ * @param name
+ * @param age
+ * @param height
+ */
+@Serializable
+class Human(private val name: String, private val age: Long, private val height: Float) {
+ init{
+ if (name == "") throw InvalidElementValueException(name)
+ if (age <= 0) throw InvalidElementValueException(age)
+ if (height <= 0) throw InvalidElementValueException(height)
+ }
+
+ override fun toString(): String {
+ return "$name, возраст $age, рост $height"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/exceptions/CollectionHasNoElementException.kt b/Programming/ProgLab6/general/src/main/kotlin/exceptions/CollectionHasNoElementException.kt
new file mode 100644
index 0000000..00a7fa6
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/exceptions/CollectionHasNoElementException.kt
@@ -0,0 +1,18 @@
+package exceptions
+
+/**
+ * Исключение, показывающее, что в коллекции нет элемента с таким [id][elements.City.id].
+ *
+ * Наследуется от класса [Exception].
+ *
+ * @param id [id] элемента типа [id][elements.City.id].
+ *
+ * @property message Сообщение об ошибке типа [String].
+ *
+ * @constructor Принимает все перечисленные выше параметры.
+ *
+ * @since 1.0
+ */
+class CollectionHasNoElementException(private val id: Long): Exception() {
+ override val message: String = "У коллекции нет элемента с id $id"
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/exceptions/CommandNotFoundException.kt b/Programming/ProgLab6/general/src/main/kotlin/exceptions/CommandNotFoundException.kt
new file mode 100644
index 0000000..2834bf5
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/exceptions/CommandNotFoundException.kt
@@ -0,0 +1,18 @@
+package exceptions
+
+/**
+ * Исключение, показывающее, что исполняемая команда не найдена.
+ *
+ * Наследуется от класса [Exception].
+ *
+ * @param command Выполняемая [command] типа [Command][commands.Command].
+ *
+ * @property message Сообщение об ошибке типа [String].
+ *
+ * @constructor Принимает все перечисленные выше параметры.
+ *
+ * @since 1.0
+ */
+class CommandNotFoundException(private val command: String): Exception() {
+ override val message: String = "Команда $command не найдена"
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/exceptions/ConnectionException.kt b/Programming/ProgLab6/general/src/main/kotlin/exceptions/ConnectionException.kt
new file mode 100644
index 0000000..a3be8e5
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/exceptions/ConnectionException.kt
@@ -0,0 +1,5 @@
+package exceptions
+
+class ConnectionException(): Exception() {
+ override val message: String = "Не удалось установить соединение."
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/exceptions/InvalidAmountOfArgumentsException.kt b/Programming/ProgLab6/general/src/main/kotlin/exceptions/InvalidAmountOfArgumentsException.kt
new file mode 100644
index 0000000..47fb872
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/exceptions/InvalidAmountOfArgumentsException.kt
@@ -0,0 +1,24 @@
+package exceptions
+
+import commands.Command
+
+/**
+ * Исключение, показывающее, что количество полученных командой аргументов отличается от ожидаемого.
+ *
+ * Наследуется от класса [Exception].
+ *
+ * @param command Выполняемая [Command][commands.Command].
+ * @param amount Количество полученных аргументов типа [Int].
+ *
+ * @property message Сообщение об ошибке типа [String].
+ *
+ * @constructor Принимает все перечисленные выше параметры.
+ *
+ * @since 1.0
+ */
+class InvalidAmountOfArgumentsException(private val command: Command, private val amount: Int): Exception() {
+ private val name: String = command.getName()
+ private val realAmount: Int = command.argumentsAmount
+
+ override val message: String = "Команда $name принимает только $realAmount аргументов, а не $amount"
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/exceptions/InvalidElementValueException.kt b/Programming/ProgLab6/general/src/main/kotlin/exceptions/InvalidElementValueException.kt
new file mode 100644
index 0000000..254bc91
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/exceptions/InvalidElementValueException.kt
@@ -0,0 +1,18 @@
+package exceptions
+
+/**
+ * Исключение, показывающее, что данное значение элемента не может быть установлено.
+ *
+ * Наследуется от класса [Exception].
+ *
+ * @param value Полученное значение.
+ *
+ * @property message Сообщение об ошибке типа [String].
+ *
+ * @constructor Принимает все перечисленные выше параметры.
+ *
+ * @since 1.0
+ */
+class InvalidElementValueException(private val value: Any): Exception() {
+ override val message: String = "Значение '$value' не может быть задано"
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/exceptions/NoNextCommandException.kt b/Programming/ProgLab6/general/src/main/kotlin/exceptions/NoNextCommandException.kt
new file mode 100644
index 0000000..74fc801
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/exceptions/NoNextCommandException.kt
@@ -0,0 +1,16 @@
+package exceptions
+
+/**
+ * Исключение, показывающее, что очередь на выполнение команд пуста.
+ *
+ * Наследуется от класса [Exception].
+ *
+ * @property message Сообщение об ошибке типа [String].
+ *
+ * @constructor Стандартный конструктор класса [Exception].
+ *
+ * @since 1.0
+ */
+class NoNextCommandException: Exception() {
+ override val message: String = "Чтение скрипта окончено"
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/general/src/main/kotlin/exceptions/ProgramExitException.kt b/Programming/ProgLab6/general/src/main/kotlin/exceptions/ProgramExitException.kt
new file mode 100644
index 0000000..b546d91
--- /dev/null
+++ b/Programming/ProgLab6/general/src/main/kotlin/exceptions/ProgramExitException.kt
@@ -0,0 +1,16 @@
+package exceptions
+
+/**
+ * Исключение, показывающее, что программа завершила работу.
+ *
+ * Наследуется от класса [Exception].
+ *
+ * @property message Сообщение об ошибке типа [String].
+ *
+ * @constructor Стандартный конструктор класса [Exception].
+ *
+ * @since 1.0
+ */
+class ProgramExitException(): Exception() {
+ override val message: String = "Завершение работы."
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/gradle.properties b/Programming/ProgLab6/gradle.properties
new file mode 100644
index 0000000..7fc6f1f
--- /dev/null
+++ b/Programming/ProgLab6/gradle.properties
@@ -0,0 +1 @@
+kotlin.code.style=official
diff --git a/Programming/ProgLab6/gradle/wrapper/gradle-wrapper.jar b/Programming/ProgLab6/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..1b33c55
Binary files /dev/null and b/Programming/ProgLab6/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Programming/ProgLab6/gradle/wrapper/gradle-wrapper.properties b/Programming/ProgLab6/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ca025c8
--- /dev/null
+++ b/Programming/ProgLab6/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/Programming/ProgLab6/gradlew b/Programming/ProgLab6/gradlew
new file mode 100644
index 0000000..23d15a9
--- /dev/null
+++ b/Programming/ProgLab6/gradlew
@@ -0,0 +1,251 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# 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 ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH="\\\"\\\""
+
+
+# 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
+ if ! command -v java >/dev/null 2>&1
+ then
+ 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
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# 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"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/Programming/ProgLab6/gradlew.bat b/Programming/ProgLab6/gradlew.bat
new file mode 100644
index 0000000..db3a6ac
--- /dev/null
+++ b/Programming/ProgLab6/gradlew.bat
@@ -0,0 +1,94 @@
+@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
+@rem SPDX-License-Identifier: Apache-2.0
+@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=.
+@rem This is normally unused
+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"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 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!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Programming/ProgLab6/server/build.gradle b/Programming/ProgLab6/server/build.gradle
new file mode 100644
index 0000000..45f1bcb
--- /dev/null
+++ b/Programming/ProgLab6/server/build.gradle
@@ -0,0 +1,16 @@
+group = 'com.leterzp.prog.server'
+version = '2.0'
+
+dependencies {
+ implementation project(':general')
+}
+
+apply plugin: 'com.gradleup.shadow'
+
+shadowJar {
+ archiveBaseName.set('ProgLab6.server')
+ archiveVersion.set('2.0')
+ manifest {
+ attributes 'Main-Class': 'ServerKt'
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/Server.kt b/Programming/ProgLab6/server/src/main/kotlin/Server.kt
new file mode 100644
index 0000000..00a2fd9
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/Server.kt
@@ -0,0 +1,10 @@
+import core.ServerInitializer
+import io.IOManager
+
+fun main() {
+ val port = 8841
+ val io = IOManager()
+ val si = ServerInitializer(port, io)
+ si.initialize()
+ si.waitForExit()
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/AddCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/AddCommand.kt
new file mode 100644
index 0000000..b30313e
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/AddCommand.kt
@@ -0,0 +1,50 @@
+package commands
+
+import core.CommandInvoker
+import elements.CityBuilder
+import exceptions.InvalidAmountOfArgumentsException
+import exceptions.InvalidElementValueException
+
+/**
+ * Команда для добавления элемента в коллекцию.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class AddCommand(override val ci: CommandInvoker): Command(ci) {
+ override val argumentsAmount = CityBuilder().size
+
+ override fun execute(arguments: List) {
+ val creator = CityBuilder()
+ if (arguments.size != creator.size) throw InvalidAmountOfArgumentsException(this, arguments.size)
+ var count = 0
+ try {
+ while (true) {
+ creator.setField(arguments[count], count)
+ if (count == creator.size-1) break
+ count++
+ }
+ ci.cm.addElement(creator.create())
+ ci.io.logger.info("Элемент успешно добавлен.")
+ result = "Элемент успешно добавлен.\n"
+ } catch (e: InvalidElementValueException) {
+ result = e.message + "\n"
+ ci.io.logger.warning(e.message)
+ }
+ }
+
+ override fun getSyntax(): String {
+ return "{element}"
+ }
+
+ override fun getName(): String {
+ return "add"
+ }
+
+ override fun describe(): String {
+ return "Добавляет элемент в коллекцию"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/ClearCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/ClearCommand.kt
new file mode 100644
index 0000000..ea99963
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/ClearCommand.kt
@@ -0,0 +1,29 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для очистки коллекции.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class ClearCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ ci.cm.clearCollection()
+ result = "Коллекция отчищена.\n"
+ ci.io.logger.info("Коллекция отчищена.")
+ }
+
+ override fun describe(): String {
+ return "Удаляет все элементы коллекции"
+ }
+
+ override fun getName(): String {
+ return "clear"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/CountGreaterThenMetersAboveSeaLevelCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/CountGreaterThenMetersAboveSeaLevelCommand.kt
new file mode 100644
index 0000000..81f7b7c
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/CountGreaterThenMetersAboveSeaLevelCommand.kt
@@ -0,0 +1,39 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для вывода количества элементов коллекции, высота над уровнем моря которых больше заданного.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class CountGreaterThenMetersAboveSeaLevelCommand(override val ci: CommandInvoker): Command(ci) {
+ override val argumentsAmount: Int = 1
+
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ try {
+ result = "Количество: " + ci.cm.countHigherThen(arguments[0].toLong()) + "\n"
+ ci.io.logger.info("Найдено количество элементов выше ${arguments[0]} метров.")
+ } catch (e: NumberFormatException) {
+ result = "Невозможно сравнить с данным значением, оно должно быть типа Long.\n"
+ ci.io.logger.warning("Неверный тип значения ${arguments[0]}.")
+ }
+ }
+
+ override fun describe(): String {
+ return "Выводит количество элементов выше заданного уровня моря"
+ }
+
+ override fun getSyntax(): String {
+ return "[metersAboveSeaLevel]"
+ }
+
+ override fun getName(): String {
+ return "count_greater_then_meters_above_sea_level"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/GroupCountingByNameCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/GroupCountingByNameCommand.kt
new file mode 100644
index 0000000..1adcf6f
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/GroupCountingByNameCommand.kt
@@ -0,0 +1,32 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для группировки по имени с подсчётом повторений.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class GroupCountingByNameCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ val names: HashMap = ci.cm.groupElements()
+ result = "Названия городов:\n"
+ for (name in names) {
+ result += " --${name.key}: ${name.value}\n"
+ }
+ ci.io.logger.info("Найден список всех элементов.")
+ }
+
+ override fun describe(): String {
+ return "Выводит количество элементов, сгруппированных по названиям"
+ }
+
+ override fun getName(): String {
+ return "group_counting_by_name"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/InfoCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/InfoCommand.kt
new file mode 100644
index 0000000..b0766ac
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/InfoCommand.kt
@@ -0,0 +1,33 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для получения информации о коллекции.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class InfoCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun describe(): String {
+ return "Выводит всю информацию о коллекции"
+ }
+
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ val time = ci.cm.initializationTime
+ val size = ci.cm.size()
+ result = "Информация о коллекции:\n"
+ result += " --Тип коллекции: java.util.Stack\n"
+ result += " --Дата инициализации коллекции: $time\n"
+ result += " --Количество элементов в коллекции: $size\n"
+ ci.io.logger.info("Найдена информация о коллекции.")
+ }
+
+ override fun getName(): String {
+ return "info"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/PingCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/PingCommand.kt
new file mode 100644
index 0000000..774b7d6
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/PingCommand.kt
@@ -0,0 +1,18 @@
+package commands
+
+import core.CommandInvoker
+
+class PingCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun describe(): String {
+ return "ping"
+ }
+
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ result = "ping\n"
+ }
+
+ override fun getName(): String {
+ return "ping"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/PrintFieldAscendingGovernmentCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/PrintFieldAscendingGovernmentCommand.kt
new file mode 100644
index 0000000..aa7b296
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/PrintFieldAscendingGovernmentCommand.kt
@@ -0,0 +1,33 @@
+package commands
+
+import core.CommandInvoker
+import elements.Government
+
+/**
+ * Команда для получения всех свойств вида [Government] в сортированном виде.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class PrintFieldAscendingGovernmentCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ val governments: List = ci.cm.getSortedGovernments()
+ result = ""
+ for (element in governments) {
+ result += element.toString()+ "\n"
+ }
+ ci.io.logger.info("Найдены все правительства.")
+ }
+
+ override fun describe(): String {
+ return "Выводит правительства городов в порядке возрастания"
+ }
+
+ override fun getName(): String {
+ return "print_field_ascending_government"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveByIdCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveByIdCommand.kt
new file mode 100644
index 0000000..0c977fa
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveByIdCommand.kt
@@ -0,0 +1,43 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для удаления элемента по id.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class RemoveByIdCommand(override val ci: CommandInvoker): Command(ci) {
+ override val argumentsAmount: Int = 1
+
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ var value: Long
+ try {
+ value = arguments[0].toLong()
+ ci.cm.removeElement(value)
+ result = "Элемент $value успешно удалён.\n"
+ ci.io.logger.info("Элемент $value удалён.")
+ }
+ catch (e: NumberFormatException) {
+ result = "${arguments[0]} не является id элемента.\n"
+ ci.io.logger.warning("Неверный id элемента.")
+ }
+ }
+
+ override fun describe(): String {
+ return "Удаляет элемент по его id"
+ }
+
+ override fun getSyntax(): String {
+ return "[id]"
+ }
+
+ override fun getName(): String {
+ return "remove_by_id"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveGreaterCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveGreaterCommand.kt
new file mode 100644
index 0000000..5dff448
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveGreaterCommand.kt
@@ -0,0 +1,40 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для удаления элементов с [id][elements.City.id] выше заданного.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class RemoveGreaterCommand(override val ci: CommandInvoker): Command(ci) {
+ override val argumentsAmount: Int = 1
+
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ try {
+ val count: Int = ci.cm.removeGreater(arguments[0].toLong())
+ result = "Удалено $count элементов.\n"
+ ci.io.logger.info("Удалено $count элементов.")
+ } catch (e: NumberFormatException) {
+ result = "${arguments[0]} не является id элемента.\n"
+ ci.io.logger.warning("Неверный id элемента.")
+ }
+ }
+
+ override fun describe(): String {
+ return "Удаляет элементы коллекции больше заданного"
+ }
+
+ override fun getSyntax(): String {
+ return "[id]"
+ }
+
+ override fun getName(): String {
+ return "remove_greater"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveLastCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveLastCommand.kt
new file mode 100644
index 0000000..123023d
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/RemoveLastCommand.kt
@@ -0,0 +1,35 @@
+package commands
+
+import core.CommandInvoker
+import exceptions.CollectionHasNoElementException
+
+/**
+ * Команда для удаления последнего элемента коллекции.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class RemoveLastCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ try {
+ ci.cm.removeLast()
+ result = "Элемент успешно удалён.\n"
+ ci.io.logger.info("Последний элемент удалён.")
+ } catch (e: CollectionHasNoElementException) {
+ result = "Последний элемент не найден: коллекция пуста.\n"
+ ci.io.logger.warning("Коллекция пуста.")
+ }
+ }
+
+ override fun describe(): String {
+ return "Удаляет последний элемент коллекции"
+ }
+
+ override fun getName(): String {
+ return "remove_last"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/ReorderCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/ReorderCommand.kt
new file mode 100644
index 0000000..ef74bc0
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/ReorderCommand.kt
@@ -0,0 +1,29 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для переворота коллекции.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class ReorderCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ ci.cm.reorderElements()
+ result = "Коллекция успешно перевёрнута.\n"
+ ci.io.logger.info("Коллекция перевернута.")
+ }
+
+ override fun describe(): String {
+ return "Переворачивает коллекцию"
+ }
+
+ override fun getName(): String {
+ return "reorder"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/SaveCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/SaveCommand.kt
new file mode 100644
index 0000000..2265869
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/SaveCommand.kt
@@ -0,0 +1,35 @@
+package commands
+
+import core.CommandInvoker
+import java.io.IOException
+
+/**
+ * Команда для сохранения коллекции в файл.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class SaveCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ try {
+ ci.cm.saveToFile()
+ result = "Коллекция успешно сохранена.\n"
+ ci.io.logger.info("Коллекция сохранена.")
+ } catch (e: IOException) {
+ result = "Файл сохранения не найден. Коллекция не сохранена.\n"
+ ci.io.logger.warning("Файл сохранения не найден.")
+ }
+ }
+
+ override fun describe(): String {
+ return "Сохраняет коллекцию в файл"
+ }
+
+ override fun getName(): String {
+ return "save"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/ShowCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/ShowCommand.kt
new file mode 100644
index 0000000..a67c75f
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/ShowCommand.kt
@@ -0,0 +1,30 @@
+package commands
+
+import core.CommandInvoker
+
+/**
+ * Команда для получения информации об элементах коллекции.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class ShowCommand(override val ci: CommandInvoker): Command(ci) {
+ override fun execute(arguments: List) {
+ super.execute(arguments)
+ val output: String = ci.cm.getAllElementsToString()
+ if (output != "") result = output + "\n"
+ else result = "Коллекция пуста.\n"
+ ci.io.logger.info("Список элементов найден.")
+ }
+
+ override fun describe(): String {
+ return "Выводит список всех элементов коллекции"
+ }
+
+ override fun getName(): String {
+ return "show"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/commands/UpdateCommand.kt b/Programming/ProgLab6/server/src/main/kotlin/commands/UpdateCommand.kt
new file mode 100644
index 0000000..8d24bbc
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/commands/UpdateCommand.kt
@@ -0,0 +1,60 @@
+package commands
+
+import core.CommandInvoker
+import elements.CityBuilder
+import exceptions.InvalidAmountOfArgumentsException
+import exceptions.InvalidElementValueException
+
+/**
+ * Команда для обновления элемента коллекции.
+ *
+ * @param ci [CommandInvoker] для [Command].
+ *
+ * @constructor Вызывает родительский конструктор класса [Command].
+ *
+ * @since 1.0
+ */
+class UpdateCommand(override val ci: CommandInvoker): Command(ci) {
+ override val argumentsAmount: Int = 1
+
+ override fun execute(arguments: List) {
+ var arguments = arguments
+ val id: Long
+ try {
+ id = arguments[0].toLong()
+ } catch (_: NumberFormatException) {
+ result = "${arguments[0]} не является id элемента.\n"
+ ci.io.logger.warning("Элемент ${arguments[0]} не найден.")
+ return
+ }
+ arguments = arguments.minus(arguments[0])
+ val creator = CityBuilder()
+ if (arguments.size != creator.size) throw InvalidAmountOfArgumentsException(this, arguments.size)
+ var count = 0
+ try {
+ while (true) {
+ creator.setField(arguments[count], count)
+ if (count == creator.size-1) break
+ count++
+ }
+ creator.update(ci.cm.getElement(id))
+ result = "Элемент успешно обновлён.\n"
+ ci.io.logger.info("Элемент обновлён.")
+ } catch (e: InvalidElementValueException) {
+ result = e.message + "\n"
+ ci.io.logger.warning(e.message)
+ }
+ }
+
+ override fun describe(): String {
+ return "Обновляет значение элемента"
+ }
+
+ override fun getSyntax(): String {
+ return "[id] {element}"
+ }
+
+ override fun getName(): String {
+ return "update"
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/core/CollectionManager.kt b/Programming/ProgLab6/server/src/main/kotlin/core/CollectionManager.kt
new file mode 100644
index 0000000..3109247
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/core/CollectionManager.kt
@@ -0,0 +1,243 @@
+package core
+
+import elements.City
+import elements.Government
+import exceptions.CollectionHasNoElementException
+import io.IOManager
+import java.io.IOException
+import java.time.LocalDate
+import java.util.Stack
+import java.util.stream.Collectors
+
+/**
+ * Класс для управления коллекцией, содержащей элементы типа [City].
+ *
+ * @param io [IOManager], с которым взаимодействует коллекция.
+ *
+ * @property initializationTime Время инициализации коллекции типа [LocalDate].
+ *
+ * @constructor Принимает все указанные выше параметры, создавая готовый к использованию объект
+ * и сразу загружая элементы из файла.
+ *
+ * @since 1.0
+ */
+class CollectionManager(val io: IOManager) {
+ private val save: String = System.getenv("SAVE_FILE") ?: "save.json"
+ private var collection: Stack = Stack()
+ val initializationTime: LocalDate = LocalDate.now()
+
+ init {
+ try {
+ collection = io.readJsonFile(save)
+ io.logger.info("Коллекция успешно загружена.")
+ } catch (e: IOException) {
+ io.logger.warning("Файл сохранения не найден.")
+ }
+ }
+
+ /**
+ * Считает количество элементов коллекции.
+ *
+ * @return Количество элементов коллекции типа [Int].
+ *
+ * @since 1.0
+ */
+ fun size(): Int {
+ io.logger.info("Поиск длины коллекции...")
+ return collection.stream().count().toInt()
+ }
+
+ /**
+ * Сохраняет хранящиеся в коллекции объекты в файл.
+ *
+ * @throws [java.io.IOException] В случае ошибки доступа к файлу.
+ *
+ * @since 1.0
+ */
+ fun saveToFile() {
+ io.logger.info("Сохранение коллекции...")
+ io.writeJsonFile(save, collection)
+ }
+
+ /**
+ * Сортирует элементы коллекции.
+ *
+ * @since 1.0
+ */
+ fun sortElements() {
+ io.logger.info("Сортировка коллекции...")
+ collection.sort()
+ }
+
+ /**
+ * Добавляет элемент в коллекцию.
+ *
+ * @param city Город типа [City], который нужно добавить в коллекцию.
+ *
+ * @since 1.0
+ */
+ fun addElement(city: City) {
+ io.logger.info("Добавление элемента...")
+ collection.push(city)
+ }
+
+ /**
+ * Переворачивает коллекцию.
+ *
+ * @since 1.0
+ */
+ fun reorderElements() {
+ io.logger.info("Переворачивание коллекции...")
+ val newCollection: Stack = Stack()
+ for (element in collection.asReversed()) {
+ newCollection.push(element)
+ }
+ collection = newCollection
+ }
+
+ /**
+ * Считает количество элементов коллекции, высота над уровнем моря которого выше заданного.
+ *
+ * @param metersAboveSeaLevel Высота над уровнем моря типа [Long].
+ *
+ * @return Количество элементов коллекции, подходящих по условию, типа [Int].
+ *
+ * @since 1.0
+ */
+ fun countHigherThen(metersAboveSeaLevel: Long): Int {
+ io.logger.info("Поиск элементов выше заданного значения...")
+ return collection.stream()
+ .filter { x -> x.metersAboveSeaLevel > metersAboveSeaLevel }
+ .count()
+ .toInt()
+ }
+
+ /**
+ * Выдаёт элемент из коллекции по [id][City.id].
+ *
+ * @param id [id][City.id] города [City].
+ *
+ * @return Элемент коллекции типа [City].
+ *
+ * @throws exceptions.CollectionHasNoElementException В случае, если в коллекции нет элемента с таким [id].
+ *
+ * @since 1.0
+ */
+ fun getElement(id: Long): City {
+ io.logger.info("Поиск элемента...")
+ try {
+ return collection.stream()
+ .filter { x -> x.id == id }
+ .collect(Collectors.toList())[0]
+ } catch (e: IndexOutOfBoundsException) {
+ throw CollectionHasNoElementException(id)
+ }
+ }
+
+ /**
+ * Выдаёт все элементы коллекции в строковом представлении.
+ *
+ * @return Все элементы коллекции типа [String].
+ *
+ * @since 1.0
+ */
+ fun getAllElementsToString(): String {
+ io.logger.info("Поиск всех элементов...")
+ return collection.stream()
+ .map { city -> city.toString() }
+ .collect(Collectors.toList())
+ .joinToString("\n")
+ }
+
+ /**
+ * Выдаёт все виды правительств элементов коллекции в сортированном виде.
+ *
+ * @return [ArrayList], содержащий все виды правительств типа [Government].
+ *
+ * @since 1.0
+ */
+ fun getSortedGovernments(): List {
+ io.logger.info("Поиск всех правительств...")
+ return collection.stream()
+ .sorted { city1, city2 -> compareValues(city1.government, city2.government) }
+ .map { city -> city.government }
+ .collect(Collectors.toList())
+ }
+
+ /**
+ * Выдаёт сгруппированные имена всех городов и количество городов с одинаковым именем.
+ *
+ * @return [HashMap] с парами элементов имя типа [String] и количество типа [Int].
+ *
+ * @since 1.0
+ */
+ fun groupElements(): HashMap {
+ io.logger.info("Группировка элементов по именам...")
+ val names: HashMap = HashMap()
+ val list = collection.stream()
+ .map { city -> city.name }
+ .collect(Collectors.toList())
+ for (name in list) {
+ names[name] = names[name] ?: 0
+ names[name] = names[name]!! + 1
+ }
+ return names
+ }
+
+ /**
+ * Удаляет элемент из коллекции по [id][City.id].
+ *
+ * @param id [id][City.id] города [City].
+ *
+ * @throws exceptions.CollectionHasNoElementException В случае, если в коллекции нет элемента с таким [id].
+ *
+ * @since 1.0
+ */
+ fun removeElement(id: Long) {
+ io.logger.info("Удаление элемента...")
+ if (!collection.remove(this.getElement(id))) throw CollectionHasNoElementException(id)
+ }
+
+ /**
+ * Удаляет последний элемент коллекции.
+ *
+ * @throws exceptions.CollectionHasNoElementException В случае, если коллекция пуста.
+ *
+ * @since 1.0
+ */
+ fun removeLast() {
+ io.logger.info("Удаление последнего элемента...")
+ if (collection.empty()) throw CollectionHasNoElementException(-1)
+ collection.remove(collection.last())
+ }
+
+ /**
+ * Удаляет элементы, [id][City.id] которых больше заданного.
+ *
+ * @param id [id][City.id] города [City].
+ *
+ * @return Количество типа [Int] удалённых элементов.
+ *
+ * @since 1.0
+ */
+ fun removeGreater(id: Long): Int {
+ io.logger.info("Удаление всех элементов больше заданного...")
+ val list = collection.stream()
+ .filter {city -> city.id > id}
+ .collect(Collectors.toList())
+ for (element in list) {
+ collection.remove(element)
+ }
+ return list.size
+ }
+
+ /**
+ * Удаляет все элементы коллекции.
+ *
+ * @since 1.0
+ */
+ fun clearCollection() {
+ io.logger.info("Отчистка коллекции...")
+ collection.clear()
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/core/CommandInvoker.kt b/Programming/ProgLab6/server/src/main/kotlin/core/CommandInvoker.kt
new file mode 100644
index 0000000..cf747d1
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/core/CommandInvoker.kt
@@ -0,0 +1,74 @@
+package core
+
+import commands.*
+import exceptions.CommandNotFoundException
+
+class CommandInvoker(val cm: CollectionManager): CommandInvokerInterface {
+ val commands: HashMap = HashMap()
+ val io = cm.io
+
+ init {
+ initializeCommand(InfoCommand(this))
+ initializeCommand(ShowCommand(this))
+ initializeCommand(AddCommand(this))
+ initializeCommand(UpdateCommand(this))
+ initializeCommand(RemoveByIdCommand(this))
+ initializeCommand(ClearCommand(this))
+ initializeCommand(RemoveLastCommand(this))
+ initializeCommand(RemoveGreaterCommand(this))
+ initializeCommand(ReorderCommand(this))
+ initializeCommand(GroupCountingByNameCommand(this))
+ initializeCommand(CountGreaterThenMetersAboveSeaLevelCommand(this))
+ initializeCommand(PrintFieldAscendingGovernmentCommand(this))
+ initializeCommand(PingCommand(this))
+ }
+
+ /**
+ * Инициализирует команду, добавляя её в список возможных к использованию.
+ *
+ * @param command Команда для инициализации, типа [Command].
+ *
+ * @since 1.0
+ */
+ fun initializeCommand(command: Command) {
+ val name: String = command.getName()
+ commands[name] = command
+ io.logger.info("Команда $name инициализирована.")
+ }
+
+ fun runOnServer(command: String) {
+ when (command.trim()) {
+ "exit" -> {
+ runOnServer("save")
+ val exit = ExitCommand(this)
+ io.logger.info("Выполняется выход.")
+ exit.execute(listOf(""))
+ }
+ "save" -> {
+ val save = SaveCommand(this)
+ io.logger.info("Выполняется сохранение.")
+ save.execute(listOf(""))
+ }
+ else -> return
+ }
+ }
+
+ override fun executeCommand(cw: CommandWrapper): CommandWrapper {
+ val command = commands[cw.command] ?: throw CommandNotFoundException(cw.command)
+ io.logger.info("Выполняется команда ${cw.name}.")
+ command.execute(cw.arguments)
+ cw.result = command.result
+ return cw
+ }
+
+ fun getAllCommandsWrapped(): List {
+ val list = mutableListOf()
+ for (command in commands.values) {
+ val wrapper = CommandWrapper()
+ wrapper.wrapCommand(command)
+ list.add(wrapper)
+ }
+ io.logger.info("Подготовлен список команд.")
+ return list.toList()
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/core/ConnectionReceiver.kt b/Programming/ProgLab6/server/src/main/kotlin/core/ConnectionReceiver.kt
new file mode 100644
index 0000000..f31de93
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/core/ConnectionReceiver.kt
@@ -0,0 +1,56 @@
+package core
+
+import commands.CommandWrapper
+import exceptions.ProgramExitException
+import java.net.DatagramPacket
+import java.net.DatagramSocket
+import kotlinx.serialization.json.Json.Default.decodeFromString
+import kotlinx.serialization.json.Json.Default.encodeToString
+import java.net.InetAddress
+
+class ConnectionReceiver(private val ci: CommandInvoker, private val port: Int) {
+ private var isWorking = true
+ private var waitingForCommand = false
+ private val socket = DatagramSocket(port, InetAddress.getLocalHost())
+ private var bytes = ByteArray(32768)
+ private val io = ci.io
+
+ fun checkConnection() {
+ if (!isWorking) throw ProgramExitException()
+ bytes = ByteArray(32768)
+ val packet = DatagramPacket(bytes, bytes.size)
+ waitingForCommand = true
+ socket.receive(packet)
+ waitingForCommand = false
+ if (isWorking) receive(packet.address, packet.port)
+ }
+
+ private fun receive(host: InetAddress, port: Int) {
+ io.logger.info("Запрос получен.")
+ var cw: CommandWrapper = decodeFromString(bytes.decodeToString().replace("\u0000", ""))
+ val result: String
+ if (cw.name == "help") {
+ val list = ci.getAllCommandsWrapped()
+ cw.result = encodeToString(list)
+ } else {
+ cw = ci.executeCommand(cw)
+ }
+ result = encodeToString(cw)
+ io.logger.info("Результат загружен.")
+ send(host, port, result)
+ }
+
+ private fun send(host: InetAddress, port: Int, response: String) {
+ bytes = response.toByteArray()
+ val packet = DatagramPacket(bytes, bytes.size, host, port)
+ socket.send(packet)
+ io.logger.info("Результат отправлен.")
+ }
+
+ fun saveInterrupt() {
+ isWorking = false
+ if (waitingForCommand) {
+ send(socket.localAddress, port, "exit")
+ }
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/core/ServerInitializer.kt b/Programming/ProgLab6/server/src/main/kotlin/core/ServerInitializer.kt
new file mode 100644
index 0000000..0e374f8
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/core/ServerInitializer.kt
@@ -0,0 +1,45 @@
+package core
+
+import exceptions.ProgramExitException
+import io.IOManager
+
+class ServerInitializer(val port: Int, val io: IOManager) {
+ val cm = CollectionManager(io)
+ val ci = CommandInvoker(cm)
+ val cr = ConnectionReceiver(ci, port)
+ lateinit var server: Thread
+ lateinit var client: Thread
+
+ fun initialize() {
+ server = Thread {
+ var isWorking = true
+ while (isWorking) {
+ try {
+ ci.runOnServer(io.readLocalCommands())
+ } catch (e: ProgramExitException) {
+ isWorking = false
+ }
+ }
+ }
+ client = Thread {
+ var isWorking = true
+ while (isWorking) {
+ try {
+ cr.checkConnection()
+ } catch (_: ProgramExitException) {
+ isWorking = false
+ }
+ }
+ }
+ server.start()
+ client.start()
+ io.logger.info("Сервер запущен.")
+ }
+
+ fun waitForExit() {
+ if (!server.isAlive && !client.isAlive) return
+ server.join()
+ cr.saveInterrupt()
+ io.logger.info("Завершение работы.")
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/server/src/main/kotlin/io/IOManager.kt b/Programming/ProgLab6/server/src/main/kotlin/io/IOManager.kt
new file mode 100644
index 0000000..c4be048
--- /dev/null
+++ b/Programming/ProgLab6/server/src/main/kotlin/io/IOManager.kt
@@ -0,0 +1,48 @@
+package io
+
+import elements.City
+import java.io.BufferedInputStream
+import java.io.BufferedWriter
+import java.io.FileInputStream
+import java.io.FileWriter
+import java.util.Stack
+import java.util.logging.FileHandler
+import java.util.logging.Logger
+import java.util.logging.SimpleFormatter
+import kotlinx.serialization.json.Json.Default.encodeToString
+import kotlinx.serialization.json.Json.Default.decodeFromString
+
+class IOManager {
+ val logger: Logger = Logger.getLogger("server")
+
+ init {
+ logger.addHandler(FileHandler("logs"))
+ logger.handlers[0].formatter = SimpleFormatter()
+ }
+
+ fun readJsonFile(file: String): Stack {
+ val reader = BufferedInputStream(FileInputStream(file))
+ val text = reader.readAllBytes().decodeToString()
+ reader.close()
+ val decodedStack: Stack = Stack()
+ if (text != "") {
+ val decodedList: List = decodeFromString>(text)
+ for (element in decodedList) {
+ decodedStack.push(element)
+ }
+ }
+ return decodedStack
+ }
+
+ fun writeJsonFile(file: String, stack: Stack) {
+ val listToEncode: List = stack.toList()
+ val text: String = encodeToString(listToEncode)
+ val writer = BufferedWriter(FileWriter(file))
+ writer.write(text)
+ writer.close()
+ }
+
+ fun readLocalCommands(): String {
+ return readln()
+ }
+}
\ No newline at end of file
diff --git a/Programming/ProgLab6/settings.gradle b/Programming/ProgLab6/settings.gradle
new file mode 100644
index 0000000..b75b507
--- /dev/null
+++ b/Programming/ProgLab6/settings.gradle
@@ -0,0 +1,7 @@
+plugins {
+ id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
+}
+rootProject.name = 'ProgLab6'
+include 'general'
+include 'client'
+include 'server'
\ No newline at end of file