Skip to content

Commit 9db7c0d

Browse files
Added base functionality to compare documents
1 parent 6276393 commit 9db7c0d

21 files changed

Lines changed: 374 additions & 114 deletions

Demos/Ktor/comparison-ktor/DocumentSamples/.gitkeep

Whitespace-only changes.

Demos/Ktor/comparison-ktor/DocumentSamples/ResultFiles/.gitkeep

Whitespace-only changes.

Demos/Ktor/comparison-ktor/src/main/kotlin/com/groupdocs/ui/Application.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.groupdocs.ui
33
import com.groupdocs.ui.config.ApplicationConfig
44
import com.groupdocs.ui.config.ServerConfig
55
import com.groupdocs.ui.di.ModulesInjection
6+
import com.groupdocs.ui.status.InternalServerException
67
import com.typesafe.config.ConfigFactory
78
import io.ktor.server.application.*
89
import io.ktor.server.config.*
@@ -67,4 +68,4 @@ fun extractComparisonConfig(): ApplicationConfig {
6768
useConfigNamingConvention = false
6869
}
6970
return hocon.decodeFromConfig(ApplicationConfig.serializer(), ConfigFactory.load("application.conf"))
70-
}
71+
}

Demos/Ktor/comparison-ktor/src/main/kotlin/com/groupdocs/ui/Defaults.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface Defaults {
2727
interface Local {
2828
companion object {
2929
const val DEFAULT_FILES_DIRECTORY = "DocumentSamples"
30-
const val DEFAULT_RESULT_DIRECTORY = "DocumentSamples/Temp"
30+
const val DEFAULT_RESULT_DIRECTORY = "ResultFiles"
3131
}
3232
}
3333

Demos/Ktor/comparison-ktor/src/main/kotlin/com/groupdocs/ui/config/ApplicationConfig.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ data class Comparison(
3636
when (filesProviderType.uppercase()) {
3737
in Defaults.Comparison.FilesProviderType.values()
3838
.map { it.name.uppercase() } -> Defaults.Comparison.FilesProviderType.valueOf(filesProviderType.uppercase())
39-
else -> Defaults.Comparison.FilesProviderType.LOCAL
39+
else -> Defaults.Comparison.DEFAULT_FILES_PROVIDER_TYPE
4040
}
4141
val tempDirectoryOrDefault: String
4242
get() = tempDirectory.ifBlank { Defaults.Comparison.DEFAULT_TEMP_DIRECTORY }

Demos/Ktor/comparison-ktor/src/main/kotlin/com/groupdocs/ui/di/ModulesInjection.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.groupdocs.ui.di
22

3-
import com.groupdocs.ui.manager.TempFolderManager
4-
import com.groupdocs.ui.manager.TempFolderManagerImpl
3+
import com.groupdocs.ui.manager.PathManager
4+
import com.groupdocs.ui.manager.PathManagerImpl
55
import com.groupdocs.ui.modules.compare.CompareController
66
import com.groupdocs.ui.modules.compare.CompareControllerImpl
77
import com.groupdocs.ui.modules.config.ConfigController
@@ -16,6 +16,8 @@ import com.groupdocs.ui.modules.tree.TreeController
1616
import com.groupdocs.ui.modules.tree.TreeControllerImpl
1717
import com.groupdocs.ui.modules.upload.UploadController
1818
import com.groupdocs.ui.modules.upload.UploadControllerImpl
19+
import com.groupdocs.ui.usecase.AreFilesSupportedUseCase
20+
import com.groupdocs.ui.usecase.CompareDocumentsUseCase
1921
import com.groupdocs.ui.usecase.GetLocalFilesUseCase
2022
import com.groupdocs.ui.usecase.RetrieveLocalFilePagesStreamUseCase
2123
import org.koin.core.module.dsl.bind
@@ -36,8 +38,10 @@ object ModulesInjection {
3638
val usecaseBeans = module {
3739
singleOf(::GetLocalFilesUseCase)
3840
singleOf(::RetrieveLocalFilePagesStreamUseCase)
41+
singleOf(::AreFilesSupportedUseCase)
42+
singleOf(::CompareDocumentsUseCase)
3943
}
4044
val managerBeans = module {
41-
singleOf(::TempFolderManagerImpl) { bind<TempFolderManager>() }
45+
singleOf(::PathManagerImpl) { bind<PathManager>() }
4246
}
4347
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.groupdocs.ui.manager
2+
3+
import com.groupdocs.ui.Defaults
4+
import com.groupdocs.ui.status.InternalServerException
5+
import org.koin.core.component.KoinComponent
6+
import java.io.File
7+
import java.nio.file.Path
8+
import java.nio.file.Paths
9+
import java.util.*
10+
import java.util.regex.Pattern
11+
import kotlin.math.min
12+
13+
/**
14+
* Manages path to temp, cache, files or result directory according to config
15+
* Does not create or check is any directory exist
16+
*/
17+
class PathManagerImpl(
18+
private val applicationConfig: com.groupdocs.ui.config.ApplicationConfig
19+
) : PathManager, KoinComponent {
20+
private val isLocalProvider: Boolean by lazy {
21+
applicationConfig.comparison.filesProviderTypeOrDefault == Defaults.Comparison.FilesProviderType.LOCAL
22+
}
23+
24+
override val tempDirectory: Path by lazy {
25+
val absoluteOrRelativeTempDirectory = Paths.get(applicationConfig.comparison.tempDirectoryOrDefault)
26+
if (absoluteOrRelativeTempDirectory.isAbsolute) {
27+
absoluteOrRelativeTempDirectory
28+
} else if (isLocalProvider) {
29+
filesDirectory.resolve(absoluteOrRelativeTempDirectory)
30+
} else {
31+
absoluteOrRelativeTempDirectory.toAbsolutePath()
32+
}.normalize()
33+
}
34+
35+
override val resultDirectory: Path by lazy {
36+
if (!isLocalProvider) {
37+
throw InternalServerException("Access to result directory is impossible, because local provider is not enabled")
38+
}
39+
val absoluteOrRelativeResultDirectory = applicationConfig.local.resultDirectoryOrDefault
40+
if (absoluteOrRelativeResultDirectory.isAbsolute) {
41+
absoluteOrRelativeResultDirectory
42+
} else {
43+
filesDirectory.resolve(absoluteOrRelativeResultDirectory).toAbsolutePath()
44+
}.normalize()
45+
}
46+
47+
override val filesDirectory: Path by lazy {
48+
if (!isLocalProvider) {
49+
throw InternalServerException("Access to files directory is impossible, because local provider is not enabled")
50+
}
51+
val absoluteOrRelativeFilesDirectory = applicationConfig.local.filesDirectoryOrDefault
52+
absoluteOrRelativeFilesDirectory.toAbsolutePath().normalize()
53+
}
54+
55+
override fun createPathForTempFile(): Path = tempDirectory.resolve("gd_${UUID.randomUUID()}.tmp")
56+
57+
override fun createPathForResultFile(sourceName: String, targetName: String, extension: String): Path {
58+
sourceName.matches(FILENAME_PATTERN) || throw InternalServerException("Source file name contains forbidden characters")
59+
targetName.matches(FILENAME_PATTERN) || throw InternalServerException("Target file name contains forbidden characters")
60+
61+
val sourceNameWithoutExtension = File(sourceName).nameWithoutExtension
62+
val targetNameWithoutExtension = File(targetName).nameWithoutExtension
63+
64+
val resultFileName = if (sourceNameWithoutExtension.length + targetNameWithoutExtension.length > MAX_FILENAME_LENGTH) {
65+
val halfOfFileNameLength = MAX_FILENAME_LENGTH / 2
66+
val trimmedSourceFileName = sourceNameWithoutExtension
67+
.substring(min(halfOfFileNameLength, sourceNameWithoutExtension.length))
68+
val trimmedTargetFileName = targetNameWithoutExtension
69+
.substring(min(halfOfFileNameLength, targetNameWithoutExtension.length))
70+
"result-${trimmedSourceFileName}-${trimmedTargetFileName}_${System.currentTimeMillis()}.$extension"
71+
} else "result-${sourceNameWithoutExtension}-${targetNameWithoutExtension}.$extension"
72+
73+
return resultDirectory.resolve(resultFileName)
74+
}
75+
76+
override fun assertPathIsInsideFilesDirectory(pathToCheck: String): Path {
77+
val checkPath = Paths.get(pathToCheck)
78+
val path = if (!checkPath.isAbsolute) {
79+
filesDirectory.resolve(pathToCheck).toAbsolutePath().normalize()
80+
} else checkPath.normalize()
81+
82+
if (!path.startsWith(filesDirectory)) {
83+
// Avoid accessing to any directory outside filesDirectory
84+
throw AccessDeniedException()
85+
}
86+
return path
87+
}
88+
89+
private companion object {
90+
val FILENAME_PATTERN = Pattern.compile("^[^</*?\"\\\\>:|]+\$").toRegex()
91+
const val MAX_FILENAME_LENGTH = 255 - 55 // 55 for extension, prefix and so on
92+
}
93+
}
94+
95+
interface PathManager {
96+
val tempDirectory: Path
97+
val resultDirectory: Path
98+
val filesDirectory: Path
99+
fun createPathForTempFile(): Path
100+
fun assertPathIsInsideFilesDirectory(pathToCheck: String): Path
101+
fun createPathForResultFile(sourceName: String, targetName: String, extension: String): Path
102+
}
103+
104+
class AccessDeniedException(message: String = "Access denied!") : InternalServerException(message)

Demos/Ktor/comparison-ktor/src/main/kotlin/com/groupdocs/ui/manager/TempFolderManager.kt

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.groupdocs.ui.model
2+
3+
data class CompareRequest(
4+
val guids: List<CompareDocument> = emptyList()
5+
)
6+
7+
data class CompareDocument(
8+
val guid: String,
9+
val password: String? = null
10+
)
Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
11
package com.groupdocs.ui.model
22

3+
import com.groupdocs.comparison.result.ChangeInfo
4+
35
data class CompareResponse(
4-
val id: Int
6+
/**
7+
* List of change information
8+
*/
9+
val changes: List<ChangeInfo>,
10+
11+
/**
12+
* List of images of pages with marked changes
13+
*/
14+
val pages: List<ComparePage>,
15+
16+
/**
17+
* Unique key of results
18+
*/
19+
val guid: String,
20+
21+
/**
22+
* Extension of compared files, for saving total results
23+
*/
24+
val extension: String = ""
525
)
26+
27+
/**
28+
* PageDescriptionEntity
29+
*
30+
* @author Aspose Pty Ltd
31+
*/
32+
data class ComparePage(
33+
val data: String? = null,
34+
val angle: Int = 0,
35+
val width: Int = 0,
36+
val height: Int = 0,
37+
val number: Int = 0,
38+
)

0 commit comments

Comments
 (0)