commit 8902b17d66269d220d6520df3f6b15b2555f88f2 Author: Jaroslav Držík Date: Sun Jan 19 11:07:46 2025 +0100 First commit diff --git a/.fleet/receipt.json b/.fleet/receipt.json new file mode 100644 index 0000000..864b22a --- /dev/null +++ b/.fleet/receipt.json @@ -0,0 +1,34 @@ +// Project generated by Kotlin Multiplatform Wizard +{ + "spec": { + "template_id": "kmt", + "targets": { + "android": { + "ui": [ + "compose" + ] + }, + "ios": { + "ui": [ + "compose" + ] + }, + "desktop": { + "ui": [ + "compose" + ] + }, + "web": { + "ui": [ + "compose" + ] + }, + "server": { + "engine": [ + "ktor" + ] + } + } + }, + "timestamp": "2025-01-14T15:21:30.946900300Z" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d9c0e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +*.iml +.kotlin +.gradle +**/build/ +xcuserdata +!src/**/build/ +local.properties +.idea +.DS_Store +captures +.externalNativeBuild +.cxx +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcodeproj/project.xcworkspace/ +!*.xcworkspace/contents.xcworkspacedata +**/xcshareddata/WorkspaceSettings.xcsettings diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b13d09 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +This is a Kotlin Multiplatform project targeting Android, iOS, Web, Desktop, Server. + +* `/composeApp` is for code that will be shared across your Compose Multiplatform applications. + It contains several subfolders: + - `commonMain` is for code that’s common for all targets. + - Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. + For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, + `iosMain` would be the right folder for such calls. + +* `/iosApp` contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform, + you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project. + +* `/server` is for the Ktor server application. + +* `/shared` is for the code that will be shared between all targets in the project. + The most important subfolder is `commonMain`. If preferred, you can add code to the platform-specific folders here too. + + +Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html), +[Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/#compose-multiplatform), +[Kotlin/Wasm](https://kotl.in/wasm/)… + +We would appreciate your feedback on Compose/Web and Kotlin/Wasm in the public Slack channel [#compose-web](https://slack-chats.kotlinlang.org/c/compose-web). +If you face any issues, please report them on [GitHub](https://github.com/JetBrains/compose-multiplatform/issues). + +You can open the web application by running the `:composeApp:wasmJsBrowserDevelopmentRun` Gradle task. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..fa2d427 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + // this is necessary to avoid the plugins to be loaded multiple times + // in each subproject's classloader + alias(libs.plugins.androidApplication) apply false + alias(libs.plugins.androidLibrary) apply false + alias(libs.plugins.composeMultiplatform) apply false + alias(libs.plugins.composeCompiler) apply false + alias(libs.plugins.kotlinJvm) apply false + alias(libs.plugins.kotlinMultiplatform) apply false + kotlin("plugin.serialization") version "1.9.22" apply false + +} + diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts new file mode 100644 index 0000000..ed86da3 --- /dev/null +++ b/composeApp/build.gradle.kts @@ -0,0 +1,122 @@ +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidApplication) + alias(libs.plugins.composeMultiplatform) + alias(libs.plugins.composeCompiler) +} + +kotlin { + androidTarget { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = "ComposeApp" + isStatic = true + } + } + + jvm("desktop") + + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + moduleName = "composeApp" + browser { + val rootDirPath = project.rootDir.path + val projectDirPath = project.projectDir.path + commonWebpackConfig { + outputFileName = "composeApp.js" + devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { + static = (static ?: mutableListOf()).apply { + // Serve sources to debug inside browser + add(rootDirPath) + add(projectDirPath) + } + } + } + } + binaries.executable() + } + + sourceSets { + val desktopMain by getting + + androidMain.dependencies { + implementation(compose.preview) + implementation(libs.androidx.activity.compose) + } + commonMain.dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.ui) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + implementation(libs.androidx.lifecycle.viewmodel) + implementation(libs.androidx.lifecycle.runtime.compose) + implementation(projects.shared) + + } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + implementation(libs.kotlinx.coroutines.swing) + } + } +} + +android { + namespace = "org.neutrino.ktans" + compileSdk = libs.versions.android.compileSdk.get().toInt() + + defaultConfig { + applicationId = "org.neutrino.ktans" + minSdk = libs.versions.android.minSdk.get().toInt() + targetSdk = libs.versions.android.targetSdk.get().toInt() + versionCode = 1 + versionName = "1.0" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + debugImplementation(compose.uiTooling) +} + +compose.desktop { + application { + mainClass = "org.neutrino.ktans.MainKt" + + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "org.neutrino.ktans" + packageVersion = "1.0.0" + } + } +} diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000..c5db0b1 --- /dev/null +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/org/neutrino/ktans/MainActivity.kt b/composeApp/src/androidMain/kotlin/org/neutrino/ktans/MainActivity.kt new file mode 100644 index 0000000..f02e290 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/org/neutrino/ktans/MainActivity.kt @@ -0,0 +1,23 @@ +package org.neutrino.ktans + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + App() + } + } +} + +@Preview +@Composable +fun AppAndroidPreview() { + App() +} \ No newline at end of file diff --git a/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml b/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..e93e11a --- /dev/null +++ b/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a571e60 Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..61da551 Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c41dd28 Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..db5080a Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..6dba46d Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..da31a87 Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..15ac681 Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b216f2d Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..f25a419 Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..e96783c Binary files /dev/null and b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/composeApp/src/androidMain/res/values/strings.xml b/composeApp/src/androidMain/res/values/strings.xml new file mode 100644 index 0000000..c7f5c2f --- /dev/null +++ b/composeApp/src/androidMain/res/values/strings.xml @@ -0,0 +1,3 @@ + + KTrans + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/compose-multiplatform.xml b/composeApp/src/commonMain/composeResources/drawable/compose-multiplatform.xml new file mode 100644 index 0000000..c0bcfb2 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/compose-multiplatform.xml @@ -0,0 +1,36 @@ + + + + + + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/neutrino/ktans/App.kt b/composeApp/src/commonMain/kotlin/org/neutrino/ktans/App.kt new file mode 100644 index 0000000..e1335bc --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/neutrino/ktans/App.kt @@ -0,0 +1,104 @@ +package org.neutrino.ktans + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.material.TextField +import androidx.compose.material.IconButton +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.filled.Clear +import db.migration.Translation + +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.ui.tooling.preview.Preview + +import ktrans.composeapp.generated.resources.Res +import ktrans.composeapp.generated.resources.compose_multiplatform +import models.TermFull +import service.DatabaseFactory.getTranslationForLanguages +import service.TermService +import service.TermServiceImpl +import service.SearchType +import service.DatabaseFactory + +@Composable +fun SearchBarTextField() { + val query = remember { mutableStateOf("") } + val termService = TermServiceImpl() + var showResults by remember { mutableStateOf(false) } + var terms: List? = null + + TextField( + value = query.value, + onValueChange = { + query.value = it + var sType = SearchType.START + sType = if (query.value.length > 2) { + SearchType.START + } else { + SearchType.EXACT + } + + val translation = getTranslationForLanguages("an","sl") ?: return@TextField + println(translation) + terms = termService.getTranslationForTerm(it,translation,sType) + showResults = terms != null + println(terms) + + }, + placeholder = { Text("Prelož...") }, + singleLine = true, + leadingIcon = { Icon(Icons.Filled.Search, contentDescription = "Search Icon") }, + trailingIcon = { + if (query.value.isNotEmpty()) { + IconButton(onClick = { query.value = "" }) { + Icon(Icons.Filled.Clear, contentDescription = "Clear Text") + } + } + }, + modifier = Modifier + .fillMaxWidth() + ) + + Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { + if (showResults && terms != null) { + for (term in terms!!) { + Text(term.string1) + Text("${term.string2},") + } + } + } +} + +@Composable +@Preview +fun App() { + DatabaseFactory.connectAndMigrate() + DatabaseFactory.connectAll() + + MaterialTheme { + var showContent by remember { mutableStateOf(false) } + Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + SearchBarTextField() + Button(onClick = { showContent = !showContent }) { + Text("Click me!") + } + AnimatedVisibility(showContent) { + val greeting = remember { Greeting().greet() } + Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + Image(painterResource(Res.drawable.compose_multiplatform), null) + Text("Compose: $greeting") + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/org/neutrino/ktans/main.kt b/composeApp/src/desktopMain/kotlin/org/neutrino/ktans/main.kt new file mode 100644 index 0000000..8e46cdb --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/org/neutrino/ktans/main.kt @@ -0,0 +1,13 @@ +package org.neutrino.ktans + +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = "KTrans", + ) { + App() + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/resources/application.conf b/composeApp/src/desktopMain/resources/application.conf new file mode 100644 index 0000000..c7314a5 --- /dev/null +++ b/composeApp/src/desktopMain/resources/application.conf @@ -0,0 +1,6 @@ +h2 { + driver = org.h2.Driver + url = "jdbc:h2:file:./data/%s;DB_CLOSE_DELAY=-1;" + user = "sa" + password = "" + } \ No newline at end of file diff --git a/composeApp/src/iosMain/kotlin/org/neutrino/ktans/MainViewController.kt b/composeApp/src/iosMain/kotlin/org/neutrino/ktans/MainViewController.kt new file mode 100644 index 0000000..baf5b9e --- /dev/null +++ b/composeApp/src/iosMain/kotlin/org/neutrino/ktans/MainViewController.kt @@ -0,0 +1,5 @@ +package org.neutrino.ktans + +import androidx.compose.ui.window.ComposeUIViewController + +fun MainViewController() = ComposeUIViewController { App() } \ No newline at end of file diff --git a/composeApp/src/wasmJsMain/kotlin/org/neutrino/ktans/main.kt b/composeApp/src/wasmJsMain/kotlin/org/neutrino/ktans/main.kt new file mode 100644 index 0000000..b70826c --- /dev/null +++ b/composeApp/src/wasmJsMain/kotlin/org/neutrino/ktans/main.kt @@ -0,0 +1,12 @@ +package org.neutrino.ktans + +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.ComposeViewport +import kotlinx.browser.document + +@OptIn(ExperimentalComposeUiApi::class) +fun main() { + ComposeViewport(document.body!!) { + App() + } +} \ No newline at end of file diff --git a/composeApp/src/wasmJsMain/resources/index.html b/composeApp/src/wasmJsMain/resources/index.html new file mode 100644 index 0000000..1470a9c --- /dev/null +++ b/composeApp/src/wasmJsMain/resources/index.html @@ -0,0 +1,12 @@ + + + + + + KTrans + + + + + + \ No newline at end of file diff --git a/composeApp/src/wasmJsMain/resources/styles.css b/composeApp/src/wasmJsMain/resources/styles.css new file mode 100644 index 0000000..0549b10 --- /dev/null +++ b/composeApp/src/wasmJsMain/resources/styles.css @@ -0,0 +1,7 @@ +html, body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..c623d2e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,13 @@ +#Kotlin +kotlin.code.style=official +kotlin.daemon.jvmargs=-Xmx2048M + +#Gradle +org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 + +#Android +android.nonTransitiveRClass=true +android.useAndroidX=true + +#Ktor +io.ktor.development=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..440ca28 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,131 @@ +[versions] +agp = "8.5.2" +android-compileSdk = "34" +android-minSdk = "24" +android-targetSdk = "34" +androidx-activityCompose = "1.9.3" +androidx-appcompat = "1.7.0" +androidx-constraintlayout = "2.2.0" +androidx-core-ktx = "1.15.0" +androidx-espresso-core = "3.6.1" +androidx-lifecycle = "2.8.4" +androidx-material = "1.12.0" +androidx-test-junit = "1.2.1" +assertjVersion = "3.26.3" +compose-multiplatform = "1.7.0" +exposedVersion = "0.55.0" +flywayVersion = "10.20.1" +h2 = "2.3.232" +hikariCpVersion = "5.1.0" +junit = "4.13.2" +junitVersion = "5.11.3" +kformat = "0.11" +kotlin = "2.1.0" +kotlinSerializationCompilerPluginEmbeddable = "1.9.22" +kotlinx-coroutines = "1.9.0" +kotlinxSerializationCore = "1.8.0" +kotlinxSerializationJson = "1.8.0" +kotlinxSerializationJsonJvm = "1.5.0" +kotlinxSerializationJsonVersion = "1.6.0" +kotlinxSerializationRuntime = "0.14.0" +kotliquery = "1.9.0" +ktor = "3.0.3" +ktorClientCio = "3.0.3" +ktorClientContentNegotiation = "3.0.3" +ktorClientContentNegotiationVersion = "3.0.3" +ktorClientCore = "3.0.3" +ktorClientLogging = "3.0.3" +ktorSerializationKotlinxCbor = "3.0.3" +ktorSerializationKotlinxJson = "3.0.3" +ktorSerializationKotlinxJsonVersion = "3.0.2" +ktorSerializationKotlinxProtobuf = "3.0.3" +ktorSerializationKotlinxXml = "3.0.3" +ktorServerContentNegotiation = "3.0.2" +ktorServerCore = "3.0.2" +ktorServerCors = "3.0.2" +ktorServerNetty = "3.0.2" +ktorServerRequestValidation = "3.0.2" +logback = "1.5.12" +material = "1.7.6" +psqlVersion = "42.7.4" +restAssuredVersion = "5.5.0" + +[libraries] +assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertjVersion" } +exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposedVersion" } +exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposedVersion" } +exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposedVersion" } +exposed-json = { module = "org.jetbrains.exposed:exposed-json", version.ref = "exposedVersion" } +flyway-core = { module = "org.flywaydb:flyway-core", version.ref = "flywayVersion" } +h2 = { module = "com.h2database:h2", version.ref = "h2" } +hikaricp = { module = "com.zaxxer:HikariCP", version.ref = "hikariCpVersion" } +io-ktor-ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" } +io-ktor-ktor-server-netty = { module = "io.ktor:ktor-server-netty" } +junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junitVersion" } +junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junitVersion" } +kformat = { module = "de.m3y.kformat:kformat", version.ref = "kformat" } +kotlin-serialization-compiler-plugin-embeddable = { module = "org.jetbrains.kotlin:kotlin-serialization-compiler-plugin-embeddable", version.ref = "kotlinSerializationCompilerPluginEmbeddable" } +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" } +androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" } +androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" } +androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } +androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" } +androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } +kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } +kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerializationCore" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } +kotlinx-serialization-json-jvm = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-jvm", version.ref = "kotlinxSerializationJsonJvm" } +kotlinx-serialization-json-v160 = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJsonVersion" } +kotlinx-serialization-runtime = { module = "org.jetbrains.kotlinx:kotlinx-serialization-runtime", version.ref = "kotlinxSerializationRuntime" } +kotliquery = { module = "com.github.seratch:kotliquery", version.ref = "kotliquery" } +ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } +ktor-client-cio-v180 = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientCio" } +ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktorClientContentNegotiation" } +ktor-client-content-negotiation-v180 = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktorClientContentNegotiationVersion" } +ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientCore" } +ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktorClientLogging" } +ktor-ktor-server-core = { module = "io.ktor:ktor-server-core" } +ktor-ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" } +ktor-serialization = { module = "io.ktor:ktor-serialization", version.ref = "ktor" } +ktor-serialization-kotlinx-cbor = { module = "io.ktor:ktor-serialization-kotlinx-cbor", version.ref = "ktorSerializationKotlinxCbor" } +ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } +ktor-serialization-kotlinx-json-v180 = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktorSerializationKotlinxJson" } +ktor-serialization-kotlinx-json-v302 = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktorSerializationKotlinxJsonVersion" } +ktor-serialization-kotlinx-protobuf = { module = "io.ktor:ktor-serialization-kotlinx-protobuf", version.ref = "ktorSerializationKotlinxProtobuf" } +ktor-serialization-kotlinx-xml = { module = "io.ktor:ktor-serialization-kotlinx-xml", version.ref = "ktorSerializationKotlinxXml" } +ktor-server-call-logging = { module = "io.ktor:ktor-server-call-logging", version.ref = "ktor" } +ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" } +ktor-server-content-negotiation-v302 = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktorServerContentNegotiation" } +ktor-server-core-jvm = { module = "io.ktor:ktor-server-core-jvm" } +ktor-server-core-v302 = { module = "io.ktor:ktor-server-core", version.ref = "ktorServerCore" } +ktor-server-cors = { module = "io.ktor:ktor-server-cors", version.ref = "ktorServerCors" } +ktor-server-default-headers = { module = "io.ktor:ktor-server-default-headers", version.ref = "ktor" } +ktor-server-netty-jvm = { module = "io.ktor:ktor-server-netty-jvm" } +ktor-server-netty-v302 = { module = "io.ktor:ktor-server-netty", version.ref = "ktorServerNetty" } +ktor-server-request-validation = { module = "io.ktor:ktor-server-request-validation", version.ref = "ktorServerRequestValidation" } +ktor-server-test-host-jvm = { module = "io.ktor:ktor-server-test-host-jvm" } +ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.ref = "ktor" } +logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } +ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" } +ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor" } +ktor-server-tests = { module = "io.ktor:ktor-server-tests-jvm", version.ref = "ktor" } +material = { module = "androidx.compose.material:material", version.ref = "material" } +postgresql = { module = "org.postgresql:postgresql", version.ref = "psqlVersion" } +rest-assured = { module = "io.rest-assured:rest-assured", version.ref = "restAssuredVersion" } + +[plugins] +androidApplication = { id = "com.android.application", version.ref = "agp" } +androidLibrary = { id = "com.android.library", version.ref = "agp" } +composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" } +composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +ktor = { id = "io.ktor.plugin", version.ref = "ktor" } +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..2c35211 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..09523c0 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..f5feea6 --- /dev/null +++ b/gradlew @@ -0,0 +1,252 @@ +#!/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 +' "$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=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + 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, 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" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9b42019 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/iosApp/Configuration/Config.xcconfig b/iosApp/Configuration/Config.xcconfig new file mode 100644 index 0000000..55ea97f --- /dev/null +++ b/iosApp/Configuration/Config.xcconfig @@ -0,0 +1,3 @@ +TEAM_ID= +BUNDLE_ID=org.neutrino.ktans.KTrans +APP_NAME=KTrans \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d6f0a20 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,393 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7555FF7B242A565900829871 /* KTrans.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KTrans.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B92378962B6B1156000C7307 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42799AB246E5F90AF97AA0EF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + AB1DB47929225F7C00F7AF9C /* Configuration */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + 42799AB246E5F90AF97AA0EF /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* KTrans.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF82242A565900829871 /* ContentView.swift */, + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iOSApp.swift */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + ); + path = iosApp; + sourceTree = ""; + }; + AB1DB47929225F7C00F7AF9C /* Configuration */ = { + isa = PBXGroup; + children = ( + AB3632DC29227652001CCB65 /* Config.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */, + 7555FF77242A565900829871 /* Sources */, + B92378962B6B1156000C7307 /* Frameworks */, + 7555FF79242A565900829871 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosApp; + packageProductDependencies = ( + ); + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* KTrans.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1540; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + packageReferences = ( + ); + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, + 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} \ No newline at end of file diff --git a/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..ee7e3ca --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..8edf56e --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "app-icon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png new file mode 100644 index 0000000..53fc536 Binary files /dev/null and b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png differ diff --git a/iosApp/iosApp/Assets.xcassets/Contents.json b/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift new file mode 100644 index 0000000..3cd5c32 --- /dev/null +++ b/iosApp/iosApp/ContentView.swift @@ -0,0 +1,21 @@ +import UIKit +import SwiftUI +import ComposeApp + +struct ComposeView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + MainViewControllerKt.MainViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} + +struct ContentView: View { + var body: some View { + ComposeView() + .ignoresSafeArea(.keyboard) // Compose has own keyboard handler + } +} + + + diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist new file mode 100644 index 0000000..412e378 --- /dev/null +++ b/iosApp/iosApp/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift new file mode 100644 index 0000000..d83dca6 --- /dev/null +++ b/iosApp/iosApp/iOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} \ No newline at end of file diff --git a/server/build.gradle.kts b/server/build.gradle.kts new file mode 100644 index 0000000..df4ee52 --- /dev/null +++ b/server/build.gradle.kts @@ -0,0 +1,51 @@ +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin.Companion.kotlinNodeJsEnvSpec + +plugins { + alias(libs.plugins.kotlinJvm) + alias(libs.plugins.ktor) + + application +} + +group = "org.neutrino.ktans" +version = "1.0.0" +application { + mainClass.set("org.neutrino.ktans.ApplicationKt") + applicationDefaultJvmArgs = listOf("-Dio.ktor.development=${extra["io.ktor.development"] ?: "false"}") +} + + +dependencies { + implementation(libs.ktor.server.core.jvm) + implementation(libs.ktor.server.netty.jvm) + implementation(libs.ktor.server.netty.jvm) + implementation(libs.kotlinx.serialization.json.jvm) + implementation(libs.kotlinx.serialization.core) + implementation(libs.ktor.server.cors) + implementation(libs.ktor.server.netty) + implementation(libs.ktor.server.core) + implementation(libs.ktor.server.request.validation) + implementation(libs.ktor.server.content.negotiation) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(projects.shared) + implementation(libs.ktor.server.call.logging) + implementation(libs.ktor.server.default.headers) + implementation(libs.ktor.server.websockets) + implementation(libs.kotlin.stdlib) + implementation(libs.kotliquery) + implementation(libs.h2) + implementation(libs.postgresql) + implementation(libs.exposed.core) + implementation(libs.exposed.dao) + implementation(libs.exposed.jdbc) + implementation(libs.exposed.json) + implementation(libs.hikaricp) + implementation(libs.flyway.core) + implementation(libs.logback) + implementation(libs.kformat) + + + testImplementation(libs.ktor.server.tests) + testImplementation(libs.kotlin.test.junit) + +} \ No newline at end of file diff --git a/server/src/main/kotlin/org/neutrino/ktans/Application.kt b/server/src/main/kotlin/org/neutrino/ktans/Application.kt new file mode 100644 index 0000000..fbf092b --- /dev/null +++ b/server/src/main/kotlin/org/neutrino/ktans/Application.kt @@ -0,0 +1,69 @@ +package org.neutrino.ktans + +import dao.DictionaryDao +import dao.PronunciationDao + +import io.ktor.serialization.kotlinx.json.* +import io.ktor.server.application.* +import io.ktor.server.netty.* +import io.ktor.server.plugins.calllogging.* +import io.ktor.server.plugins.contentnegotiation.* +import io.ktor.server.plugins.defaultheaders.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import org.jetbrains.exposed.sql.transactions.transaction +import service.DatabaseFactory + +import util.JsonMapper +import web.index +import service.DatabaseFactory.dbExecId +import service.DatabaseFactory.dbExecName +import service.DictTypeService +import service.DictTypeServiceImpl +import service.DictionaryServiceImpl +import service.PronunciationServiceImpl +import service.LanguageServiceImpl +import service.TranslationServiceImpl +import service.TermServiceImpl +import web.dictType +import web.dictionary +import web.pronunciation +import web.language +import web.translation +import web.term + +fun Application.module() { + install(DefaultHeaders) + install(CallLogging) + + install(ContentNegotiation) { + json(JsonMapper.defaultMapper) + } + + + DatabaseFactory.connectAndMigrate() + DatabaseFactory.connectAll() + + + val dictTypeService = DictTypeServiceImpl() + val dictionaryService = DictionaryServiceImpl() + val pronunciationService = PronunciationServiceImpl() + val languageService = LanguageServiceImpl() + val translationService = TranslationServiceImpl() + val termService = TermServiceImpl() + + routing { + index() + dictType(dictTypeService) + dictionary(dictionaryService) + pronunciation(pronunciationService) + language(languageService) + translation(translationService,termService) + term(termService) + } + +} + +fun main(args: Array) { + EngineMain.main(args) +} \ No newline at end of file diff --git a/server/src/main/resources/application.conf b/server/src/main/resources/application.conf new file mode 100644 index 0000000..7a7a8e1 --- /dev/null +++ b/server/src/main/resources/application.conf @@ -0,0 +1,17 @@ +ktor { + deployment { + port = 8080 + watch = [ build ] + } + + application { + modules = [ org.neutrino.ktans.ApplicationKt.module ] + } +} + +h2 { + driver = org.h2.Driver + url = "jdbc:h2:file:./data/%s;DB_CLOSE_DELAY=-1;" + user = "sa" + password = "" + } \ No newline at end of file diff --git a/server/src/main/resources/index.html b/server/src/main/resources/index.html new file mode 100644 index 0000000..e390543 --- /dev/null +++ b/server/src/main/resources/index.html @@ -0,0 +1,53 @@ + + + + + + Kotlin/Ktor/Exposed Starter - It's Working! + + + + +

It's Working!

+

This starter project creates a new in-memory H2 database with one table for Widget instances. A simple RESTful interface is provided + to perform CRUD operations on Widgets alongside a websocket to be notified in real-time of any changes.

+

Routes:

+

GET /widgets --> get all widgets in the database

+

GET /widgets/{id} --> get one widget instance by id (integer)

+

POST /widgets --> add a new widget to the database by providing a JSON object (converted to a NewWidget instance). + e.g -

+
{
+    "name": "new widget",
+    "quantity": 64
+}
+
+

returns

+
{
+    "id": 4,
+    "name": "new widget",
+    "quantity": 64,
+    "dateCreated": 1519926898
+}
+
+

PUT /widgets --> update an existing widgets name or quantity. Pass in the id in the JSON request to determine which record to update +

+

DELETE /widgets/{id} --> delete the widget with the specified id

+

Notifications (WebSocket)

+

All updates (creates, updates and deletes) to Widget instances are served as notifications through a WebSocket endpoint:

+

WS /updates --> returns Notification instances containing the change type, id and entity (if applicable) e.g:

+
{
+    "type": "CREATE",
+    "id": 12,
+    "entity": {
+      "id": 12,
+      "name": "widget1",
+      "quantity": 5,
+      "dateUpdated": 1533583858169
+    }
+}
+
+ + + + + diff --git a/server/src/main/resources/logback.xml b/server/src/main/resources/logback.xml new file mode 100644 index 0000000..3e11d78 --- /dev/null +++ b/server/src/main/resources/logback.xml @@ -0,0 +1,12 @@ + + + + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..79a9fbc --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,33 @@ +rootProject.name = "KTrans" +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +pluginManagement { + repositories { + google { + mavenContent { + includeGroupAndSubgroups("androidx") + includeGroupAndSubgroups("com.android") + includeGroupAndSubgroups("com.google") + } + } + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositories { + google { + mavenContent { + includeGroupAndSubgroups("androidx") + includeGroupAndSubgroups("com.android") + includeGroupAndSubgroups("com.google") + } + } + mavenCentral() + } +} + +include(":composeApp") +include(":server") +include(":shared") \ No newline at end of file diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts new file mode 100644 index 0000000..6d54cf9 --- /dev/null +++ b/shared/build.gradle.kts @@ -0,0 +1,114 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig + +val ktorVersion = "3.0.2" +val exposedVersion = "0.55.0" +val h2Version = "2.3.232" +val hikariCpVersion = "5.1.0" +val flywayVersion = "10.20.1" +val logbackVersion = "1.5.12" +val assertjVersion = "3.26.3" +val restAssuredVersion = "5.5.0" +val junitVersion = "5.11.3" +val psqlVersion = "42.7.4" + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidLibrary) + kotlin("plugin.serialization") + +} + +repositories { + mavenCentral() +} + +dependencies { + +} + + +kotlin { + androidTarget { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + iosX64() + iosArm64() + iosSimulatorArm64() + + jvm() + + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + browser { + val rootDirPath = project.rootDir.path + val projectDirPath = project.projectDir.path + commonWebpackConfig { + devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { + static = (static ?: mutableListOf()).apply { + // Serve sources to debug inside browser + add(rootDirPath) + add(projectDirPath) + } + } + } + } + } + + sourceSets { + commonMain.dependencies { + // put your Multiplatform dependencies here + implementation(libs.kotlinx.serialization.json.jvm) + implementation(libs.kotlinx.serialization.core) + + implementation(libs.io.ktor.ktor.server.core) + implementation(libs.ktor.serialization) + implementation(libs.ktor.ktor.server.netty) + implementation(libs.ktor.server.call.logging) + implementation(libs.ktor.server.default.headers) + implementation(libs.ktor.server.websockets) + implementation(libs.ktor.server.content.negotiation) + + implementation(libs.kotlinx.coroutines.core) + + + implementation(libs.ktor.serialization) + implementation(libs.kotlinx.serialization.core) + + implementation(libs.kotlin.stdlib) + implementation(libs.kotliquery) + implementation(libs.h2) + implementation(libs.postgresql) + + implementation(libs.exposed.core) + implementation(libs.exposed.dao) + implementation(libs.exposed.jdbc) + implementation(libs.exposed.json) + implementation(libs.hikaricp) + implementation(libs.flyway.core) + implementation(libs.logback) + implementation(libs.kformat) + + } + } +} + + + +android { + namespace = "org.neutrino.ktans.shared" + compileSdk = libs.versions.android.compileSdk.get().toInt() + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + defaultConfig { + minSdk = libs.versions.android.minSdk.get().toInt() + } +} diff --git a/shared/src/androidMain/kotlin/org/neutrino/ktans/Platform.android.kt b/shared/src/androidMain/kotlin/org/neutrino/ktans/Platform.android.kt new file mode 100644 index 0000000..6244646 --- /dev/null +++ b/shared/src/androidMain/kotlin/org/neutrino/ktans/Platform.android.kt @@ -0,0 +1,9 @@ +package org.neutrino.ktans + +import android.os.Build + +class AndroidPlatform : Platform { + override val name: String = "Android ${Build.VERSION.SDK_INT}" +} + +actual fun getPlatform(): Platform = AndroidPlatform() \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/Constants.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/Constants.kt new file mode 100644 index 0000000..cc66126 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/Constants.kt @@ -0,0 +1,3 @@ +package org.neutrino.ktans + +const val SERVER_PORT = 8080 \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/Greeting.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/Greeting.kt new file mode 100644 index 0000000..9b83203 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/Greeting.kt @@ -0,0 +1,9 @@ +package org.neutrino.ktans + +class Greeting { + private val platform = getPlatform() + + fun greet(): String { + return "Hello, ${platform.name}!" + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/Platform.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/Platform.kt new file mode 100644 index 0000000..e39ae00 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/Platform.kt @@ -0,0 +1,7 @@ +package org.neutrino.ktans + +interface Platform { + val name: String +} + +expect fun getPlatform(): Platform \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/DictTypeDao.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/DictTypeDao.kt new file mode 100644 index 0000000..19f1802 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/DictTypeDao.kt @@ -0,0 +1,18 @@ +package dao + +import org.jetbrains.exposed.dao.* +import org.jetbrains.exposed.dao.id.EntityID +import tables.DictTypes +import models.DictType +class DictTypeDao(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(DictTypes) + + var shortName by DictTypes.shortName + var fullName by DictTypes.fullName + + fun toModel() = DictType( + id = id.value, + shortName = shortName, + fullName = fullName + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/DictionaryDao.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/DictionaryDao.kt new file mode 100644 index 0000000..6c1100c --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/DictionaryDao.kt @@ -0,0 +1,25 @@ +package dao + +import dao.SuffixDao.Companion.referrersOn +import models.Language +import org.jetbrains.exposed.dao.* +import org.jetbrains.exposed.dao.id.EntityID +import tables.* +import models.Dictionary + +class DictionaryDao(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(Dictionaries) + + var name by Dictionaries.name + var fullName by Dictionaries.fullName + var lang1 by LanguageDao referencedOn Dictionaries.lang1 + var lang2 by LanguageDao referencedOn Dictionaries.lang2 + + fun toModel(): Dictionary = Dictionary( + id = id.value, + name = name, + fullName = fullName, + lang1Id = lang1.id.value, + lang2Id = lang2.id.value + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/LanguageDao.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/LanguageDao.kt new file mode 100644 index 0000000..e907d92 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/LanguageDao.kt @@ -0,0 +1,18 @@ +package dao + +import org.jetbrains.exposed.dao.* +import org.jetbrains.exposed.dao.id.EntityID +import tables.* +import models.Language +class LanguageDao(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(Languages) + + var name by Languages.name + var shortName by Languages.shortName + + fun toModel() : Language = Language( + id = id.value, + name = name, + shortName = shortName + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/PronunciationDao.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/PronunciationDao.kt new file mode 100644 index 0000000..c636c67 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/PronunciationDao.kt @@ -0,0 +1,22 @@ +package dao + +import org.jetbrains.exposed.dao.* +import org.jetbrains.exposed.dao.id.EntityID +import tables.Pronunciations +import models.Pronunciation + + +class PronunciationDao(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(Pronunciations) + + var ipa by Pronunciations.ipa + var filename by Pronunciations.filename + var type by PronunciationTypeDao referencedOn Pronunciations.typeId + + fun toModel() : Pronunciation = Pronunciation( + id = id.value, + typeId = type.id.value, + ipa = ipa, + filename = filename + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/PronunciationTypeDao.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/PronunciationTypeDao.kt new file mode 100644 index 0000000..03e9a13 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/PronunciationTypeDao.kt @@ -0,0 +1,12 @@ +package dao + +import models.Translation +import org.jetbrains.exposed.dao.* +import org.jetbrains.exposed.dao.id.EntityID +import tables.PronunciationTypes + +class PronunciationTypeDao(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(PronunciationTypes) + + var name by PronunciationTypes.name +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/SuffixDao.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/SuffixDao.kt new file mode 100644 index 0000000..8cc13be --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/SuffixDao.kt @@ -0,0 +1,16 @@ +package dao + +import org.jetbrains.exposed.dao.* +import org.jetbrains.exposed.dao.id.EntityID +import tables.Suffixes +import models.Suffix + +class SuffixDao(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(Suffixes) + + var text by Suffixes.text + fun toModel(): Suffix = Suffix( + id = id.value, + text = text + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/TermDao.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/TermDao.kt new file mode 100644 index 0000000..66a6571 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/TermDao.kt @@ -0,0 +1,47 @@ +package dao + +import dao.SuffixDao.Companion.referrersOn +import models.Language +import org.jetbrains.exposed.dao.* +import org.jetbrains.exposed.dao.id.EntityID +import tables.* +import models.Term +import models.TermFull + +class TermDao(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(Terms) + + var string1 by Terms.string1 + var string2 by Terms.string2 + var suffix1 by SuffixDao optionalReferencedOn Terms.suffix1 + var suffix2 by SuffixDao optionalReferencedOn Terms.suffix2 + var type by DictTypeDao optionalReferencedOn Terms.type + var member by Terms.member + var order2 by Terms.order2 + //var flags by Terms.flags + var pronunciations by PronunciationDao via TermsPronunciations + + fun toModel(): Term = Term( + id = id.value, + string1 = string1, + string2 = string2, + typeId = type?.id?.value, + suffix1Id = suffix1?.id?.value, + suffix2Id = suffix2?.id?.value, + member = member, + order2 = order2 + //flags = flags + ) + fun toFullModel(): TermFull = TermFull( + id = id.value, + string1 = string1, + string2 = string2, + type = type?.toModel(), + suffix1 = suffix1?.toModel(), + suffix2 = suffix2?.toModel(), + member = member, + order2 = order2, + //flags = flags, + pronunciations = pronunciations.map { it.toModel() } + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/TranslationDao.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/TranslationDao.kt new file mode 100644 index 0000000..d6ea655 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/dao/TranslationDao.kt @@ -0,0 +1,27 @@ +package dao + +import models.Translation +import org.jetbrains.exposed.dao.* +import org.jetbrains.exposed.dao.id.EntityID +import tables.Translations + +class TranslationDao(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(Translations) + + var langName1 by Translations.langName1 + var langName2 by Translations.langName2 + var direction by Translations.direction + var dictionary by DictionaryDao referencedOn Translations.dictionaryId + var lang1 by LanguageDao referencedOn Translations.lang1Id + var lang2 by LanguageDao referencedOn Translations.lang2Id + + fun toModel() : Translation = Translation( + id = id.value, + dictionaryId = dictionary.id.value, + lang1Id = lang1.id.value, + lang2Id = lang2.id.value, + lang1Name = langName1, + lang2Name = langName2, + direction = direction + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V1__test_connection.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V1__test_connection.kt new file mode 100644 index 0000000..3508ca4 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V1__test_connection.kt @@ -0,0 +1,197 @@ +package db.migration + +import org.flywaydb.core.api.migration.BaseJavaMigration +import org.flywaydb.core.api.migration.Context +import org.jetbrains.exposed.sql.SchemaUtils +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.collections.* +import kotliquery.* +import de.m3y.kformat.* +import de.m3y.kformat.Table.Hints +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.Json + +val format = Json { encodeDefaults = true } + +data class Dictionary( + val id: Int, + val lang1Id: Int, + val lang2Id: Int, + val name: String, + val fullName: String) + +data class DictType( + val id: Int, + val shortName: String, + val fullName: String +) +data class Language( + val id: Int, + val name: String, + val shortName: String +) +data class Translation( + val id: Int, + val dictionaryId: Int, + val lang1Id: Int, + val lang2Id: Int, + val lang1Name: String, + val lang2Name: String, + val direction: Int +) + +data class Pronunciation( + val id: Int, + val typeId: Int, + val ipa: String?, + val filename: String? +) + +data class PronunciationType( + val id: Int, + val name: String? +) +data class TermsPronunciation( + val termId: Int, + val pronunciationId: Int, +) + +data class Suffix( + val id: Int, + val text: String +) + +data class Term( + val id: Int, + val dictionaryId: Int, + val string1: String, + val string2: String, + val suffix1Id: Int?, + val suffix2Id: Int?, + val typeId: Int?, + val member: String?, + val order2: Int?, + val flags: String? +) +val toDictionary: (Row) -> Dictionary = { row -> + Dictionary( + row.int("id"), + row.int("lang1_id"), + row.int("lang2_id"), + row.string("name"), + row.string("full_name") + ) +} + +val toDictType: (Row) -> DictType = { row -> + DictType( + row.int("id"), + row.string("short_name"), + row.string("full_name") + ) +} +val toLanguage: (Row) -> Language = { row -> + Language( + row.int("id"), + row.string("name"), + row.string("short_name") + ) +} +val toTranslation: (Row) -> Translation = { row -> + Translation( + row.int("id"), + row.int("dictionary_id"), + row.int("lang1_id"), + row.int("lang2_id"), + row.string("lang_name1"), + row.string("lang_name2"), + row.int("direction"), + ) +} + +val toPronunciation: (Row) -> Pronunciation = { row -> + Pronunciation( + row.int("id"), + row.int("type_id"), + row.stringOrNull("ipa"), + row.stringOrNull("filename") + ) +} + +val toTermsPronunciation: (Row) -> TermsPronunciation = { row -> + TermsPronunciation( + row.int("term_id"), + row.int("pronunciation_id"), + ) +} + +val toSuffix: (Row) -> Suffix = { row -> + Suffix( + row.int("id"), + row.string("text") + ) +} +val toTerm: (Row) -> Term = { row -> + Term( + row.int("id"), + row.int("dictionary_id"), + row.string("string1"), + row.string("string2"), + row.intOrNull("suffix1_id"), + row.intOrNull("suffix2_id"), + row.intOrNull("type_id"), + row.stringOrNull("member"), + row.intOrNull("order2"), + row.stringOrNull("flags") + ) +} + +object SharedData { + var hashDict: Map = mapOf() + var hashDictType: Map = mapOf() + var hashLang: Map = mapOf() + var hashTransMap: Map = mapOf() + var hashPron: Map = mapOf() + var hashSuffix: Map = mapOf() + var hashTermPron : MutableMap> = mutableMapOf() + var listTerm : List = listOf() + var getListofTerms : (Int) -> List = { + listOf() + } +} + +class V1__test_connection: BaseJavaMigration() { + override fun migrate(context: Context?) { + val session = sessionOf("jdbc:postgresql://nuc.lan:5432/dict", "dict_user", "PW4dbdict") + val allNameQuery = queryOf("select * from dictionary").map(toDictionary).asList + val allDict = session.run(allNameQuery) + SharedData.hashDict = allDict.associateBy { it.id } + val allDictTypeQuery = queryOf("select * from dict_type").map(toDictType).asList + val allDictType = session.run(allDictTypeQuery) + SharedData.hashDictType = allDictType.associateBy { it.id } + val allTranslationQuery = queryOf("select * from translation").map(toTranslation).asList + val allTranslation = session.run(allTranslationQuery) + SharedData.hashTransMap = allTranslation.associateBy { it.id } + val allLangQuery = queryOf("select * from language").map(toLanguage).asList + val allLanguages = session.run(allLangQuery) + SharedData.hashLang = allLanguages.associateBy { it.id } + val allSuffixQuery = queryOf("select * from suffix").map(toSuffix).asList + val allSuffix = session.run(allSuffixQuery) + SharedData.hashSuffix = allSuffix.associateBy { it.id } + val allPronunciationQuery = queryOf("select * from pronunciation").map(toPronunciation).asList + val allPronunciation = session.run(allPronunciationQuery) + SharedData.hashPron = allPronunciation.associateBy { it.id } + val allTermsPronunciationQuery = queryOf("select * from terms_pronunciations").map(toTermsPronunciation).asList + val allTermsPronunciation = session.run(allTermsPronunciationQuery) + allTermsPronunciation.forEach { + val p = SharedData.hashTermPron.getOrDefault(it.termId, arrayOf()) + SharedData.hashTermPron[it.termId] = p + it.pronunciationId + } + SharedData.getListofTerms = { + println("DB ID = ${it}") + session.run(queryOf("select * from term WHERE dictionary_id=${it}").map(toTerm).asList) + } + + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V2__crete_settingsdb.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V2__crete_settingsdb.kt new file mode 100644 index 0000000..8234b34 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V2__crete_settingsdb.kt @@ -0,0 +1,53 @@ +package db.migration + + +import kotlinx.serialization.decodeFromString +import tables.* +import org.flywaydb.core.api.migration.BaseJavaMigration +import org.flywaydb.core.api.migration.Context +import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.sql.SchemaUtils +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.upsert + +class V2__create_settingsdb: BaseJavaMigration() { + override fun migrate(context: Context?) { + transaction { + SchemaUtils.create(Dictionaries, Languages, Translations, Settings) + + for ((di, lang) in SharedData.hashLang) { + Languages.insert { + it[id] = lang.id + it[name] = lang.name + it[shortName] = lang.shortName + } + } + + for ((di, dict) in SharedData.hashDict) { + Dictionaries.insert { + it[id] = dict.id + it[lang1] = dict.lang1Id + it[lang2] = dict.lang2Id + it[name] = dict.name + it[fullName] = dict.fullName + } + } + + + + for ((di, trans) in SharedData.hashTransMap) { + Translations.insert { + it[id] = trans.id + it[lang1Id] = trans.lang1Id + it[lang2Id] = trans.lang2Id + it[dictionaryId] = trans.dictionaryId + it[langName1] = trans.lang1Name + it[langName2] = trans.lang2Name + it[direction] = trans.direction + } + } + + } + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V3__create_dictionaries.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V3__create_dictionaries.kt new file mode 100644 index 0000000..0932c60 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/db/migration/V3__create_dictionaries.kt @@ -0,0 +1,145 @@ +package db.migration + +import dao.DictionaryDao +import kotlinx.serialization.decodeFromString +import tables.* +import org.flywaydb.core.api.migration.BaseJavaMigration +import org.flywaydb.core.api.migration.Context +import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.system.exitProcess + +class V3__create_dictionaries: BaseJavaMigration() { + override fun migrate(context: Context?) { + println(SharedData.hashDict) + //exitProcess(0) + for ((dbId,dict) in SharedData.hashDict) { + + var dictTerms = SharedData.getListofTerms(dbId) + val dbName = "jdbc:h2:file:/Users/jaro/data/dict_${SharedData.hashLang[dict.lang1Id]?.shortName?.lowercase()}${SharedData.hashLang[dict.lang2Id]?.shortName?.lowercase()}" + println(dbName) + val db = Database.connect(dbName) + transaction (db) { + SchemaUtils.create(Dictionaries, Languages, Translations, TermsPronunciations, Pronunciations, PronunciationTypes, DictTypes, Suffixes, Terms) + + for ((di, lang) in SharedData.hashLang) { + Languages.insert { + it[id] = lang.id + it[name] = lang.name + it[shortName] = lang.shortName + } + } + + for ((di, d) in SharedData.hashDict) { + Dictionaries.insert { + it[id] = d.id + it[lang1] = d.lang1Id + it[lang2] = d.lang2Id + it[name] = d.name + it[fullName] = d.fullName + } + } + + for ((di, trans) in SharedData.hashTransMap) { + Translations.insert { + it[id] = trans.id + it[lang1Id] = trans.lang1Id + it[lang2Id] = trans.lang2Id + it[dictionaryId] = trans.dictionaryId + it[langName1] = trans.lang1Name + it[langName2] = trans.lang2Name + it[direction] = trans.direction + } + } + + PronunciationTypes.insert { + it[id] = 1 + it[name] = "English" + } + + PronunciationTypes.insert { + it[id] = 2 + it[name] = "American english" + } + + PronunciationTypes.insert { + it[id] = 3 + it[name] = "Business english" + } + + for ((di, dt) in SharedData.hashDictType) { + DictTypes.insert { + it[id] = dt.id + it[shortName] = dt.shortName + it[fullName] = dt.fullName + } + } + + var suffixMap : MutableMap = mutableMapOf() + for (t in dictTerms) { + + if (t.suffix1Id != null && !suffixMap.containsKey(t.suffix1Id)) { + val ss = SharedData.hashSuffix.getValue(t.suffix1Id) + Suffixes.insert { + it[id] = ss.id + it[text] = ss.text + } + suffixMap[ss.id] = true + } + + if (t.suffix2Id != null && !suffixMap.containsKey(t.suffix2Id)) { + val ss = SharedData.hashSuffix.getValue(t.suffix2Id) + Suffixes.insert { + it[id] = ss.id + it[text] = ss.text + } + suffixMap[ss.id] = true + } + + Terms.insert { + it[id] = t.id + it[string1] = t.string1 + it[string2] = t.string2 + it[suffix1] = t.suffix1Id + it[suffix2] = t.suffix2Id + it[type] = t.typeId + it[member] = t.member + it[order2] = t.order2 + //it[flags] = format.decodeFromString(t.flags?.toString() ?:"[]") + } + + if (SharedData.hashTermPron.containsKey(t.id)) { + + for (pronId in SharedData.hashTermPron.getValue(t.id)){ + val pron = SharedData.hashPron.getValue(pronId) + Pronunciations.upsert { + it[id] = pron.id + it[typeId] = pron.typeId + it[ipa] = pron.ipa + it[filename] = pron.filename + } + } + + } + } + + for (t in dictTerms) { + if (SharedData.hashTermPron.containsKey(t.id)) { + for (pronId in SharedData.hashTermPron.getValue(t.id)){ + TermsPronunciations.insert { + it[term] = t.id + it[pronunciation] = pronId + } + } + } + } + + dictTerms = listOf() + suffixMap = mutableMapOf() + + } + + } + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/DictType.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/DictType.kt new file mode 100644 index 0000000..e0e29ff --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/DictType.kt @@ -0,0 +1,14 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + + + +@Serializable +data class DictType( + val id: Int, + val shortName: String, + val fullName: String + ) + diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Dictionary.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Dictionary.kt new file mode 100644 index 0000000..58cccb9 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Dictionary.kt @@ -0,0 +1,17 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + + + +@Serializable +data class Dictionary( + val id: Int, + val lang1Id: Int, + val lang2Id: Int, + val name: String?, + val fullName: String?, + val translations: List = emptyList() +) + diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Language.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Language.kt new file mode 100644 index 0000000..72dd624 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Language.kt @@ -0,0 +1,15 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + + + +@Serializable +data class Language( + val id: Int, + val name: String, + val shortName: String +) + + diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Pronunciation.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Pronunciation.kt new file mode 100644 index 0000000..684c57f --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Pronunciation.kt @@ -0,0 +1,12 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + +@Serializable +data class Pronunciation( + val id: Int, + val typeId: Int, + val ipa: String?, + val filename: String? +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/PronunciationType.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/PronunciationType.kt new file mode 100644 index 0000000..3c601f4 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/PronunciationType.kt @@ -0,0 +1,10 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + +@Serializable +data class PronunciationType( + val id: Int, + val name: String? +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Setting.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Setting.kt new file mode 100644 index 0000000..429db90 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Setting.kt @@ -0,0 +1,12 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + +@Serializable +data class Settings( + val id: Int, + val dictionary: Int?, + val lastSearch: String?, +) + diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Suffix.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Suffix.kt new file mode 100644 index 0000000..8a89529 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Suffix.kt @@ -0,0 +1,11 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + + +@Serializable +data class Suffix( + val id: Int, + val text: String +) diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Term.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Term.kt new file mode 100644 index 0000000..a403cfc --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Term.kt @@ -0,0 +1,33 @@ +package models + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonObject +import org.jetbrains.exposed.sql.Table + + +@Serializable +data class Term( + val id: Int, + val string1: String, + val string2: String, + val typeId: Int?, + val suffix1Id: Int?, + val suffix2Id: Int?, + val member: String?, + val order2: Int?, + // val flags: IntArray? +) + +@Serializable +data class TermFull( + val id: Int, + val string1: String, + val string2: String, + val type: DictType?, + val suffix1: Suffix?, + val suffix2: Suffix?, + val member: String?, + val order2: Int?, + //val flags: IntArray?, + val pronunciations: List? +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/TermsPronunciation.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/TermsPronunciation.kt new file mode 100644 index 0000000..e986a50 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/TermsPronunciation.kt @@ -0,0 +1,11 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + + +@Serializable +data class TermsPronunciation( + val termsId: Int, + val pronunciationId: Int +) diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Translation.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Translation.kt new file mode 100644 index 0000000..2015013 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/models/Translation.kt @@ -0,0 +1,17 @@ +package models + +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Table + + + +@Serializable +data class Translation( + val id: Int, + val dictionaryId: Int, + val lang1Id: Int, + val lang2Id: Int, + val lang1Name: String?, + val lang2Name: String?, + val direction: Int +) diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DatabaseFactory.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DatabaseFactory.kt new file mode 100644 index 0000000..30baca4 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DatabaseFactory.kt @@ -0,0 +1,107 @@ +package service + +import com.zaxxer.hikari.HikariConfig +import com.zaxxer.hikari.HikariDataSource +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.flywaydb.core.Flyway +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.slf4j.LoggerFactory +import javax.sql.DataSource +import tables.* +import dao.* +import com.typesafe.config.ConfigFactory +import models.Translation + +object DatabaseFactory { + + private val log = LoggerFactory.getLogger(this::class.java) + private val dbs : MutableMap = mutableMapOf() + private val hdb : MutableMap = mutableMapOf() + private val htrans: MutableMap = mutableMapOf() + + fun connectAndMigrate() { + log.info("Initialising database") + val pool = hikari() + dbs[0] = Database.connect(pool) + runFlyway(pool) + } + + private fun hikari(): HikariDataSource { + val cfg = ConfigFactory.load().getConfig("h2") + val config = HikariConfig().apply { + driverClassName = cfg.getString("driver") + jdbcUrl = String.format(cfg.getString("url"),"dict_settings") + maximumPoolSize = 3 + isAutoCommit = false + transactionIsolation = "TRANSACTION_REPEATABLE_READ" + validate() + } + + return HikariDataSource(config) + } + + fun getTranslationForLanguages(l1: String, l2: String): Translation? = htrans["${l1}${l2}"]?: null + + fun connectAll(): Map { + val hMap : MutableMap = mutableMapOf() + transaction { + for (trans in TranslationDao.all()) { + val l1 = trans.lang1.shortName.lowercase() + val l2 = trans.lang2.shortName.lowercase() + + htrans["${l1}${l2}"] = trans.toModel() + } + + for (dict in DictionaryDao.all()) { + val cfg = ConfigFactory.load().getConfig("h2") + val config = HikariConfig().apply { + driverClassName = cfg.getString("driver") + jdbcUrl = String.format(cfg.getString("url"),"dict_${dict.lang1.shortName.lowercase()}${dict.lang2.shortName.lowercase()}") + maximumPoolSize = 3 + isAutoCommit = false + transactionIsolation = "TRANSACTION_REPEATABLE_READ" + validate() + } + val db = HikariDataSource(config) + val dbc = Database.connect(db) + dbs[dict.id.value] = dbc + hdb["${dict.lang1.shortName.lowercase()}${dict.lang2.shortName.lowercase()}"] = dbc + } + } + return hMap + } + + private fun runFlyway(datasource: DataSource) { + val flyway = Flyway.configure().dataSource(datasource).load() + try { + flyway.info() + flyway.migrate() + } catch (e: Exception) { + log.error("Exception running flyway migration", e) + throw e + } + log.info("Flyway migration has finished") + } + + suspend fun dbExec( + block: () -> T + ): T = withContext(Dispatchers.IO) { + transaction { block() } + } + + fun dbExecId( + dbId: Int, block: () -> T + ) : T = transaction(dbs[dbId]) { + block() + } + + fun dbExecName( + d1: String, d2: String, block: () -> T + ) : T = transaction(hdb["${d1.lowercase()}${d2.lowercase()}"]) { + block() + } + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictTypeService.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictTypeService.kt new file mode 100644 index 0000000..7d3bc43 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictTypeService.kt @@ -0,0 +1,9 @@ +package service + +import dao.DictTypeDao +import models.DictType + +interface DictTypeService { + fun getDictType(id: Int): DictType? + fun getAllDictTypes(): List +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictTypeServiceImpl.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictTypeServiceImpl.kt new file mode 100644 index 0000000..da1092a --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictTypeServiceImpl.kt @@ -0,0 +1,17 @@ +package service + +import dao.DictTypeDao +import org.jetbrains.exposed.sql.transactions.transaction +import service.DatabaseFactory.dbExecId +import models.DictType +class DictTypeServiceImpl : DictTypeService { + override fun getDictType(id: Int): DictType? = dbExecId(1) { + DictTypeDao.findById(id)?.toModel() + } + + + override fun getAllDictTypes(): List = dbExecId(1) { + DictTypeDao.all().map { it.toModel() } + } + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictionaryService.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictionaryService.kt new file mode 100644 index 0000000..fb4d91d --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictionaryService.kt @@ -0,0 +1,9 @@ +package service + +import dao.DictionaryDao +import models.Dictionary + +interface DictionaryService { + fun getDictionary(id: Int): Dictionary? + fun getAllDictionary(): List +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictionaryServiceImpl.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictionaryServiceImpl.kt new file mode 100644 index 0000000..1327d4f --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/DictionaryServiceImpl.kt @@ -0,0 +1,16 @@ +package service + +import dao.DictionaryDao +import org.jetbrains.exposed.sql.transactions.transaction +import service.DatabaseFactory.dbExecId +import models.Dictionary +class DictionaryServiceImpl : DictionaryService { + override fun getDictionary(id: Int): Dictionary? = dbExecId(1) { + DictionaryDao.findById(id)?.toModel() + } + + override fun getAllDictionary(): List = dbExecId(1) { + DictionaryDao.all().map { it.toModel() } + } + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/LanguageService.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/LanguageService.kt new file mode 100644 index 0000000..3d0815e --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/LanguageService.kt @@ -0,0 +1,9 @@ +package service + +import dao.LanguageDao +import models.Language + +interface LanguageService { + fun getLanguage(id: Int): Language? + fun getAllLanguages(): List +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/LanguageServiceImpl.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/LanguageServiceImpl.kt new file mode 100644 index 0000000..7891b08 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/LanguageServiceImpl.kt @@ -0,0 +1,17 @@ +package service + +import dao.LanguageDao +import org.jetbrains.exposed.sql.transactions.transaction +import service.DatabaseFactory.dbExecId +import models.Language + +class LanguageServiceImpl : LanguageService { + override fun getLanguage(id: Int): Language? = dbExecId(1) { + LanguageDao.findById(id)?.toModel() + } + + override fun getAllLanguages(): List = dbExecId(1) { + LanguageDao.all().map { it.toModel() } + } + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/PronunciationService.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/PronunciationService.kt new file mode 100644 index 0000000..a20bde9 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/PronunciationService.kt @@ -0,0 +1,9 @@ +package service + +import dao.PronunciationDao +import models.Pronunciation + +interface PronunciationService { + fun getPronunciation(id: Int): Pronunciation? + fun getAllPronunciations(): List +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/PronunciationServiceImpl.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/PronunciationServiceImpl.kt new file mode 100644 index 0000000..8bb224c --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/PronunciationServiceImpl.kt @@ -0,0 +1,15 @@ +package service + +import dao.PronunciationDao +import models.Pronunciation +import service.DatabaseFactory.dbExecId + +class PronunciationServiceImpl: PronunciationService { + override fun getPronunciation(id: Int): Pronunciation? = dbExecId(1) { + PronunciationDao.findById(id)?.toModel() + } + + override fun getAllPronunciations(): List = dbExecId(1) { + PronunciationDao.all().map { it.toModel() } + } +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TermService.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TermService.kt new file mode 100644 index 0000000..85104ef --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TermService.kt @@ -0,0 +1,24 @@ +package service + +import dao.TermDao +import models.Term +import models.TermFull +import models.Pronunciation +import models.Translation + +enum class SearchType { + EXACT, + START, + END, + FULL +} + +interface TermService { + fun getTerm(id: Int): Term? + fun getFullTerm(id: Int): TermFull? + fun getAllTerm(): List + fun getPronunciationsForTerm(id: Int): List? + fun getTranslationForTerm(term: String, trans: Translation, type: SearchType = SearchType.EXACT): List? + fun getTranslationForTerm(term: String, fromLang: String, toLang: String): List? + +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TermServiceImpl.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TermServiceImpl.kt new file mode 100644 index 0000000..6e86b15 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TermServiceImpl.kt @@ -0,0 +1,59 @@ +package service + +import dao.TermDao +import org.jetbrains.exposed.sql.transactions.transaction +import service.DatabaseFactory.dbExecId +import service.DatabaseFactory.dbExecName +import models.Term +import models.TermFull +import models.Pronunciation +import models.Translation +import tables.* +import service.SearchType + +class TermServiceImpl : TermService { + override fun getTerm(id: Int): Term? = dbExecId(1) { + TermDao.findById(id)?.toModel() + } + + override fun getAllTerm(): List = dbExecId(1) { + TermDao.all().map { it.toModel() } + } + + override fun getFullTerm(id: Int): TermFull? = dbExecId(1) { + TermDao.findById(id)?.toFullModel() + } + + override fun getPronunciationsForTerm(id: Int): List? = dbExecId(1) { + TermDao.findById(id)?.toFullModel()?.pronunciations + } + + override fun getTranslationForTerm(term: String,trans: Translation, type: SearchType): List? { + val s : String + when (type) { + SearchType.START -> s = term + "%" + SearchType.END -> s = "%" + term + SearchType.FULL -> s = "%" + term + "%" + SearchType.EXACT -> s = term + } + + return dbExecId(trans.dictionaryId) { + if (trans.direction == 1) + if (type == SearchType.EXACT) + TermDao.find { Terms.string1 eq s }.map { it.toFullModel() } + else + TermDao.find { Terms.string1 like s }.map { it.toFullModel() } + else + if (type == SearchType.EXACT) + TermDao.find { Terms.string2 eq s }.map { it.toFullModel() } + else + TermDao.find { Terms.string1 like s }.map { it.toFullModel() } + } + } + + override fun getTranslationForTerm(term: String, fromLang: String, toLang: String): List? = dbExecName(fromLang,toLang) { + TermDao.find { Terms.string1 eq term }.map { it.toFullModel() } + } + + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TranslationService.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TranslationService.kt new file mode 100644 index 0000000..8a33b64 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TranslationService.kt @@ -0,0 +1,10 @@ +package service + +import dao.TranslationDao +import models.Translation + +interface TranslationService { + fun getTranslation(id: Int): Translation? + fun getTranslationFromTo(from: String, to: String): Translation? + fun getAllTranslations(): List +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TranslationServiceImpl.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TranslationServiceImpl.kt new file mode 100644 index 0000000..510c2c4 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/service/TranslationServiceImpl.kt @@ -0,0 +1,24 @@ +package service + +import dao.TranslationDao +import dao.LanguageDao +import org.jetbrains.exposed.sql.transactions.transaction +import service.DatabaseFactory.dbExecId +import service.DatabaseFactory.getTranslationForLanguages +import models.Translation +import tables.Translations +import tables.Languages + +class TranslationServiceImpl : TranslationService { + override fun getTranslation(id: Int): Translation? = dbExecId(0) { + TranslationDao.findById(id)?.toModel() + } + + override fun getAllTranslations(): List = dbExecId(0) { + TranslationDao.all().map { it.toModel() } + } + + override fun getTranslationFromTo(from: String, to: String): Translation? = dbExecId(0) { + getTranslationForLanguages(from,to) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/DictType.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/DictType.kt new file mode 100644 index 0000000..0112ffb --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/DictType.kt @@ -0,0 +1,8 @@ +package tables + +import org.jetbrains.exposed.dao.id.IntIdTable + +object DictTypes : IntIdTable() { + val shortName = varchar("short_name", 255) + val fullName = varchar("full_name", 255) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Dictionary.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Dictionary.kt new file mode 100644 index 0000000..89d3019 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Dictionary.kt @@ -0,0 +1,10 @@ +package tables + +import org.jetbrains.exposed.dao.id.IntIdTable + +object Dictionaries : IntIdTable() { + val lang1 = reference("lang1_id", Languages) + val lang2 = reference("lang2_id", Languages) + val name = varchar("name", 255) + val fullName = varchar("full_name", 255) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Language.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Language.kt new file mode 100644 index 0000000..aba9740 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Language.kt @@ -0,0 +1,12 @@ +package tables + +import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.Column + +object Languages : IntIdTable() { + + val name = varchar("name", 255) + val shortName = varchar("short_name", 255) + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Pronunciation.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Pronunciation.kt new file mode 100644 index 0000000..345f423 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Pronunciation.kt @@ -0,0 +1,10 @@ +package tables + +import org.jetbrains.exposed.dao.id.IntIdTable + +object Pronunciations : IntIdTable() { + val typeId = reference("type_id",PronunciationTypes) + + val ipa = varchar("ipa", 255).nullable() + val filename = varchar("filename", 255).nullable() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/PronunciationType.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/PronunciationType.kt new file mode 100644 index 0000000..4727799 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/PronunciationType.kt @@ -0,0 +1,7 @@ +package tables + +import org.jetbrains.exposed.dao.id.IntIdTable + +object PronunciationTypes : IntIdTable() { + val name = varchar("name", 255) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Setting.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Setting.kt new file mode 100644 index 0000000..854b6c6 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Setting.kt @@ -0,0 +1,8 @@ +package tables + +import org.jetbrains.exposed.dao.id.IntIdTable + +object Settings : IntIdTable() { + val dictionary = reference("dictionary_id",Dictionaries) + val lastSearch = varchar("last_search", 255).nullable() +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Suffix.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Suffix.kt new file mode 100644 index 0000000..5dea41a --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Suffix.kt @@ -0,0 +1,7 @@ +package tables + +import org.jetbrains.exposed.dao.id.IntIdTable + +object Suffixes : IntIdTable() { + val text = varchar("text", 255) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Term.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Term.kt new file mode 100644 index 0000000..9a6434e --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Term.kt @@ -0,0 +1,17 @@ +package tables + +import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.json.json +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import tables.Suffixes +object Terms : IntIdTable() { + val string1 = varchar("string1", 255) + val string2 = varchar("string2", 255) + val suffix1 = reference("suffix1_id",Suffixes).nullable() + val suffix2 = reference("suffix2_id",Suffixes).nullable() + val type = reference("type_id",DictTypes).nullable() + val member = varchar("member", 255).nullable() + val order2 = integer("order2").nullable() + //val flags = json("flags", Json.Default).nullable() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/TermsPronunciation.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/TermsPronunciation.kt new file mode 100644 index 0000000..c3aa2b4 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/TermsPronunciation.kt @@ -0,0 +1,10 @@ +package tables + +import org.jetbrains.exposed.sql.Table + +object TermsPronunciations : Table() { + val term = reference("term", Terms) + val pronunciation = reference("pronunciation", Pronunciations) + + override val primaryKey = PrimaryKey(term, pronunciation, name = "PK_Term_Pronunciation") +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Translation.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Translation.kt new file mode 100644 index 0000000..923957e --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/tables/Translation.kt @@ -0,0 +1,13 @@ +package tables + +import org.jetbrains.exposed.dao.id.IntIdTable + +object Translations : IntIdTable() { + val dictionaryId = reference("dictionary_id",Dictionaries) + val lang1Id = reference("lang1_id",Languages) + val lang2Id = reference("lang2_id",Languages) + + val langName1 = varchar("lang_name1", 255) + val langName2 = varchar("lang_name2", 255) + val direction = integer("direction") +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/util/JsonMapper.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/util/JsonMapper.kt new file mode 100644 index 0000000..21b29a8 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/util/JsonMapper.kt @@ -0,0 +1,11 @@ +package util + +import kotlinx.serialization.json.Json + +object JsonMapper { + + val defaultMapper = Json { + prettyPrint = true + } + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/web/DictTypeResource.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/DictTypeResource.kt new file mode 100644 index 0000000..abc6596 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/DictTypeResource.kt @@ -0,0 +1,29 @@ +package web + +import io.ktor.http.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import io.ktor.websocket.* +import service.DictTypeService +import service.DictTypeServiceImpl + +fun Route.dictType(dictTypeService: DictTypeServiceImpl) { + + route("/dict-type") { + + get { + call.respond(dictTypeService.getAllDictTypes()) + } + + get("/{id}") { + val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id") + val dictType = dictTypeService.getDictType(id) + if (dictType == null) call.respond(HttpStatusCode.NotFound) + else call.respond(dictType) + } + + } + +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/web/DictionaryResource.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/DictionaryResource.kt new file mode 100644 index 0000000..e3af989 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/DictionaryResource.kt @@ -0,0 +1,29 @@ +package web + +import io.ktor.http.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import io.ktor.websocket.* +import service.DictionaryServiceImpl +import service.DictionaryService + +fun Route.dictionary(dictionaryService: DictionaryServiceImpl) { + + route("/dictionary") { + + get { + call.respond(dictionaryService.getAllDictionary()) + } + + get("/{id}") { + val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id") + val dictionary = dictionaryService.getDictionary(id) + if (dictionary == null) call.respond(HttpStatusCode.NotFound) + else call.respond(dictionary) + } + + } + +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/web/IndexResource.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/IndexResource.kt new file mode 100644 index 0000000..31191ba --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/IndexResource.kt @@ -0,0 +1,14 @@ +package web + +import io.ktor.http.* +import io.ktor.server.response.* +import io.ktor.server.routing.* + +fun Route.index() { + + val indexPage = javaClass.getResource("/index.html").readText() + + get("/") { + call.respondText(indexPage, ContentType.Text.Html) + } +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/web/LanguageResource.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/LanguageResource.kt new file mode 100644 index 0000000..3d4126d --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/LanguageResource.kt @@ -0,0 +1,29 @@ +package web + +import io.ktor.http.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import io.ktor.websocket.* +import service.LanguageServiceImpl +import service.LanguageService + +fun Route.language(LanguageService: LanguageServiceImpl) { + + route("/language") { + + get { + call.respond(LanguageService.getAllLanguages()) + } + + get("/{id}") { + val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id") + val language = LanguageService.getLanguage(id) + if (language == null) call.respond(HttpStatusCode.NotFound) + else call.respond(language) + } + + } + +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/web/PronunciationResource.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/PronunciationResource.kt new file mode 100644 index 0000000..36b0eaf --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/PronunciationResource.kt @@ -0,0 +1,29 @@ +package web + +import io.ktor.http.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import io.ktor.websocket.* +import service.PronunciationServiceImpl +import service.DictionaryService + +fun Route.pronunciation(pronunciationService: PronunciationServiceImpl) { + + route("/pronunciation") { + + get { + call.respond(pronunciationService.getAllPronunciations()) + } + + get("/{id}") { + val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id") + val pronunciation = pronunciationService.getPronunciation(id) + if (pronunciation == null) call.respond(HttpStatusCode.NotFound) + else call.respond(pronunciation) + } + + } + +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/web/TermResource.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/TermResource.kt new file mode 100644 index 0000000..1b2b891 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/TermResource.kt @@ -0,0 +1,37 @@ +package web + +import io.ktor.http.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import io.ktor.websocket.* +import service.TermServiceImpl +import service.TermService + +fun Route.term(TermService: TermServiceImpl) { + + route("/term") { + + get { + call.respond(TermService.getAllTerm()) + } + + get("/{id}") { + val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id") + val term = TermService.getTerm(id) + if (term == null) call.respond(HttpStatusCode.NotFound) + else call.respond(term) + } + + get("/{id}/full") { + val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id") + val term = TermService.getFullTerm(id) + + if (term == null) call.respond(HttpStatusCode.NotFound) + else call.respond(term) + } + + } + +} diff --git a/shared/src/commonMain/kotlin/org/neutrino/ktans/web/TranslationResource.kt b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/TranslationResource.kt new file mode 100644 index 0000000..0a04d9d --- /dev/null +++ b/shared/src/commonMain/kotlin/org/neutrino/ktans/web/TranslationResource.kt @@ -0,0 +1,61 @@ +package web + +import io.ktor.http.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import io.ktor.websocket.* +import service.TranslationServiceImpl +import service.TranslationService +import service.TermServiceImpl +import service.TermService +import service.DatabaseFactory.getTranslationForLanguages +import service.SearchType + + +fun Route.translation(TranslationService: TranslationServiceImpl,TermService: TermServiceImpl) { + + route("/translation") { + + get { + call.respond(TranslationService.getAllTranslations()) + } + + get("/{id}") { + val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id") + val Translation = TranslationService.getTranslation(id) + if (Translation == null) call.respond(HttpStatusCode.NotFound) + else call.respond(Translation) + } + + get("/{from}/{to}/{term}") { + val from = call.parameters["from"]?: throw IllegalStateException("Must provide from") + val to = call.parameters["to"]?: throw IllegalStateException("Must provide to") + val term = call.parameters["term"]?: throw IllegalStateException("Must provide term") + + val translation = getTranslationForLanguages(from,to) + if (translation == null) call.respond(HttpStatusCode.NotFound) + else { + val terms = TermService.getTranslationForTerm(term,translation) + if (terms == null) call.respond(HttpStatusCode.NotFound) + else call.respond(terms) + } + } + + get("/{from}/{to}/{term}/all") { + val from = call.parameters["from"]?: throw IllegalStateException("Must provide from") + val to = call.parameters["to"]?: throw IllegalStateException("Must provide to") + val term = call.parameters["term"]?: throw IllegalStateException("Must provide term") + + val translation = getTranslationForLanguages(from,to) + if (translation == null) call.respond(HttpStatusCode.NotFound) + else { + val terms = TermService.getTranslationForTerm(term,translation,SearchType.START) + if (terms == null) call.respond(HttpStatusCode.NotFound) + else call.respond(terms) + } + } + } + +} diff --git a/shared/src/iosMain/kotlin/org/neutrino/ktans/Platform.ios.kt b/shared/src/iosMain/kotlin/org/neutrino/ktans/Platform.ios.kt new file mode 100644 index 0000000..72783cd --- /dev/null +++ b/shared/src/iosMain/kotlin/org/neutrino/ktans/Platform.ios.kt @@ -0,0 +1,9 @@ +package org.neutrino.ktans + +import platform.UIKit.UIDevice + +class IOSPlatform: Platform { + override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion +} + +actual fun getPlatform(): Platform = IOSPlatform() \ No newline at end of file diff --git a/shared/src/jvmMain/kotlin/org/neutrino/ktans/Platform.jvm.kt b/shared/src/jvmMain/kotlin/org/neutrino/ktans/Platform.jvm.kt new file mode 100644 index 0000000..9db8eaa --- /dev/null +++ b/shared/src/jvmMain/kotlin/org/neutrino/ktans/Platform.jvm.kt @@ -0,0 +1,7 @@ +package org.neutrino.ktans + +class JVMPlatform: Platform { + override val name: String = "Java ${System.getProperty("java.version")}" +} + +actual fun getPlatform(): Platform = JVMPlatform() \ No newline at end of file diff --git a/shared/src/wasmJsMain/kotlin/org/neutrino/ktans/Platform.wasmJs.kt b/shared/src/wasmJsMain/kotlin/org/neutrino/ktans/Platform.wasmJs.kt new file mode 100644 index 0000000..f112875 --- /dev/null +++ b/shared/src/wasmJsMain/kotlin/org/neutrino/ktans/Platform.wasmJs.kt @@ -0,0 +1,7 @@ +package org.neutrino.ktans + +class WasmPlatform: Platform { + override val name: String = "Web with Kotlin/Wasm" +} + +actual fun getPlatform(): Platform = WasmPlatform() \ No newline at end of file