Merge commit '1d57fb83a4db05f5a6b4c997d4bbbfc74e8c7d04' as 'Programming/ProgLab6'
This commit is contained in:
@@ -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
|
||||||
Generated
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
Generated
+29
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<compositeConfiguration>
|
||||||
|
<compositeBuild compositeDefinitionSource="IDE">
|
||||||
|
<builds>
|
||||||
|
<build path="$PROJECT_DIR$/client" name="client" />
|
||||||
|
</builds>
|
||||||
|
</compositeBuild>
|
||||||
|
</compositeConfiguration>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/client" />
|
||||||
|
<option value="$PROJECT_DIR$/general" />
|
||||||
|
<option value="$PROJECT_DIR$/server" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$/client" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Kotlin2JvmCompilerArguments">
|
||||||
|
<option name="jvmTarget" value="1.8" />
|
||||||
|
</component>
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="2.2.0" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17 (2)" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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 "[<null> | command]"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describe(): String {
|
||||||
|
return "Выводит информацию о всех командах либо описание одной команды"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String, CommandWrapper> = HashMap()
|
||||||
|
private val nextArgument: Stack<String> = Stack<String>()
|
||||||
|
val executionHistory = Stack<String>()
|
||||||
|
|
||||||
|
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<String> = 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<String>()
|
||||||
|
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<String> = 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<String>): List<String> {
|
||||||
|
if (command.argumentsAmount > 1) {
|
||||||
|
val args = mutableListOf<String>()
|
||||||
|
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<CommandWrapper> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<CommandWrapper> {
|
||||||
|
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<CommandWrapper>(bytes.decodeToString().replace("\u0000", ""))
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
throw ConnectionException()
|
||||||
|
}
|
||||||
|
} catch (_: ConnectionException) {
|
||||||
|
if (i == 3) {
|
||||||
|
throw ConnectionException()
|
||||||
|
}
|
||||||
|
io.write("Не удалось установить соединение с сервером. Попытка ${i+1} из 3.\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package io
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Типы возможных способов чтения данных, используемых программой.
|
||||||
|
*/
|
||||||
|
enum class IOType {FILE, CONSOLE}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
group = 'com.leterzp.prog.general'
|
||||||
|
version = '2.0'
|
||||||
@@ -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<String>) {
|
||||||
|
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<String>, count: Int = 0): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class CommandWrapper() {
|
||||||
|
var command: String = ""
|
||||||
|
var argumentsAmount = 0
|
||||||
|
var arguments: List<String> = 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<String>): Boolean {
|
||||||
|
if (args.size == argumentsAmount) return true
|
||||||
|
if (args.size == 1) {
|
||||||
|
try {
|
||||||
|
args[0].toLong()
|
||||||
|
return true
|
||||||
|
} catch (_: NumberFormatException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
super.execute(arguments)
|
||||||
|
throw ProgramExitException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describe(): String {
|
||||||
|
return "Завершает работу программы"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(): String {
|
||||||
|
return "exit"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import commands.CommandWrapper
|
||||||
|
|
||||||
|
interface CommandInvokerInterface {
|
||||||
|
fun executeCommand(cw: CommandWrapper): CommandWrapper
|
||||||
|
}
|
||||||
@@ -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<City> {
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String> = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package elements
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Перечисление возможных климатов для города [City].
|
||||||
|
*/
|
||||||
|
enum class Climate {RAIN_FOREST, MONSOON, HUMIDCONTINENTAL, SUBARCTIC, TUNDRA}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package elements
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Перечисления возможных видов правительства для города [City].
|
||||||
|
*/
|
||||||
|
enum class Government{ ARISTOCRACY, ANARCHY, KLEPTOCRACY, CORPORATOCRACY, JUNTA}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -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"
|
||||||
|
}
|
||||||
@@ -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 не найдена"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package exceptions
|
||||||
|
|
||||||
|
class ConnectionException(): Exception() {
|
||||||
|
override val message: String = "Не удалось установить соединение."
|
||||||
|
}
|
||||||
+24
@@ -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"
|
||||||
|
}
|
||||||
+18
@@ -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' не может быть задано"
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Исключение, показывающее, что очередь на выполнение команд пуста.
|
||||||
|
*
|
||||||
|
* Наследуется от класса [Exception].
|
||||||
|
*
|
||||||
|
* @property message Сообщение об ошибке типа [String].
|
||||||
|
*
|
||||||
|
* @constructor Стандартный конструктор класса [Exception].
|
||||||
|
*
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class NoNextCommandException: Exception() {
|
||||||
|
override val message: String = "Чтение скрипта окончено"
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Исключение, показывающее, что программа завершила работу.
|
||||||
|
*
|
||||||
|
* Наследуется от класса [Exception].
|
||||||
|
*
|
||||||
|
* @property message Сообщение об ошибке типа [String].
|
||||||
|
*
|
||||||
|
* @constructor Стандартный конструктор класса [Exception].
|
||||||
|
*
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class ProgramExitException(): Exception() {
|
||||||
|
override val message: String = "Завершение работы."
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
kotlin.code.style=official
|
||||||
Binary file not shown.
@@ -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
|
||||||
Vendored
+251
@@ -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" "$@"
|
||||||
Vendored
+94
@@ -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
|
||||||
@@ -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'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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 "Добавляет элемент в коллекцию"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
super.execute(arguments)
|
||||||
|
ci.cm.clearCollection()
|
||||||
|
result = "Коллекция отчищена.\n"
|
||||||
|
ci.io.logger.info("Коллекция отчищена.")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describe(): String {
|
||||||
|
return "Удаляет все элементы коллекции"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(): String {
|
||||||
|
return "clear"
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
super.execute(arguments)
|
||||||
|
val names: HashMap<String, Int> = 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
super.execute(arguments)
|
||||||
|
result = "ping\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(): String {
|
||||||
|
return "ping"
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -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<String>) {
|
||||||
|
super.execute(arguments)
|
||||||
|
val governments: List<Government?> = 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
super.execute(arguments)
|
||||||
|
ci.cm.reorderElements()
|
||||||
|
result = "Коллекция успешно перевёрнута.\n"
|
||||||
|
ci.io.logger.info("Коллекция перевернута.")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describe(): String {
|
||||||
|
return "Переворачивает коллекцию"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(): String {
|
||||||
|
return "reorder"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<City> = Stack<City>()
|
||||||
|
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<City> = Stack<City>()
|
||||||
|
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<Government?> {
|
||||||
|
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<String, Int> {
|
||||||
|
io.logger.info("Группировка элементов по именам...")
|
||||||
|
val names: HashMap<String, Int> = 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import commands.*
|
||||||
|
import exceptions.CommandNotFoundException
|
||||||
|
|
||||||
|
class CommandInvoker(val cm: CollectionManager): CommandInvokerInterface {
|
||||||
|
val commands: HashMap<String, Command> = 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<CommandWrapper> {
|
||||||
|
val list = mutableListOf<CommandWrapper>()
|
||||||
|
for (command in commands.values) {
|
||||||
|
val wrapper = CommandWrapper()
|
||||||
|
wrapper.wrapCommand(command)
|
||||||
|
list.add(wrapper)
|
||||||
|
}
|
||||||
|
io.logger.info("Подготовлен список команд.")
|
||||||
|
return list.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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("Завершение работы.")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<City> {
|
||||||
|
val reader = BufferedInputStream(FileInputStream(file))
|
||||||
|
val text = reader.readAllBytes().decodeToString()
|
||||||
|
reader.close()
|
||||||
|
val decodedStack: Stack<City> = Stack<City>()
|
||||||
|
if (text != "") {
|
||||||
|
val decodedList: List<City> = decodeFromString<List<City>>(text)
|
||||||
|
for (element in decodedList) {
|
||||||
|
decodedStack.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decodedStack
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeJsonFile(file: String, stack: Stack<City>) {
|
||||||
|
val listToEncode: List<City> = stack.toList()
|
||||||
|
val text: String = encodeToString(listToEncode)
|
||||||
|
val writer = BufferedWriter(FileWriter(file))
|
||||||
|
writer.write(text)
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readLocalCommands(): String {
|
||||||
|
return readln()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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'
|
||||||
Reference in New Issue
Block a user