diff --git a/build.gradle b/build.gradle index 2ab56408..c9db367b 100644 --- a/build.gradle +++ b/build.gradle @@ -36,8 +36,8 @@ buildConfig { buildConfigField 'String', 'PROFILE_URL', 'https://sourcerer.io/' // App version. - buildConfigField 'int', 'VERSION_CODE', '3' - buildConfigField 'String', 'VERSION', '0.1.0' + buildConfigField 'int', 'VERSION_CODE', '4' + buildConfigField 'String', 'VERSION', '0.1.1' // Logging. buildConfigField 'String', 'ENV', project.hasProperty('env') ? env : 'production' diff --git a/src/main/kotlin/app/Logger.kt b/src/main/kotlin/app/Logger.kt index 3428cd79..b80633f0 100644 --- a/src/main/kotlin/app/Logger.kt +++ b/src/main/kotlin/app/Logger.kt @@ -93,7 +93,8 @@ object Logger { } private fun configLevelValue() : Int { - val a = mapOf("trace" to TRACE, "debug" to DEBUG, "info" to INFO, "warn" to WARN, "error" to ERROR) + val a = mapOf("trace" to TRACE, "debug" to DEBUG, "info" to INFO, + "warn" to WARN, "error" to ERROR) return a.getValue(BuildConfig.LOG_LEVEL) } diff --git a/src/main/kotlin/app/Main.kt b/src/main/kotlin/app/Main.kt index 110f0cb5..2306dc27 100644 --- a/src/main/kotlin/app/Main.kt +++ b/src/main/kotlin/app/Main.kt @@ -11,6 +11,7 @@ import app.utils.CommandConfig import app.utils.CommandAdd import app.utils.CommandList import app.utils.CommandRemove +import app.utils.FileHelper.toPath import app.utils.Options import app.utils.PasswordHelper import app.utils.RepoHelper @@ -75,9 +76,9 @@ class Main(argv: Array) { } private fun doAdd(commandAdd: CommandAdd) { - val path = commandAdd.path + val path = commandAdd.path?.toPath() if (path != null && RepoHelper.isValidRepo(path)) { - val localRepo = LocalRepo(path) + val localRepo = LocalRepo(path.toString()) localRepo.hashAllContributors = commandAdd.hashAll configurator.addLocalRepoPersistent(localRepo) configurator.saveToFile() diff --git a/src/main/kotlin/app/config/FileConfigurator.kt b/src/main/kotlin/app/config/FileConfigurator.kt index c90ebad2..6373a0e7 100644 --- a/src/main/kotlin/app/config/FileConfigurator.kt +++ b/src/main/kotlin/app/config/FileConfigurator.kt @@ -212,7 +212,7 @@ class FileConfigurator : Configurator { try { loadConfig = Files.newBufferedReader(FileHelper - .getPath(CONFIG_FILE_NAME)).use { + .toPath(CONFIG_FILE_NAME)).use { mapper.readValue(it, Config::class.java) } } catch (e: IOException) { @@ -241,7 +241,7 @@ class FileConfigurator : Configurator { */ override fun saveToFile() { try { - Files.newBufferedWriter(FileHelper.getPath(CONFIG_FILE_NAME)).use { + Files.newBufferedWriter(FileHelper.toPath(CONFIG_FILE_NAME)).use { mapper.writeValue(it, persistent) } } catch (e: IOException) { diff --git a/src/main/kotlin/app/extractors/Extractor.kt b/src/main/kotlin/app/extractors/Extractor.kt index 4fbc9d3b..4e46cb1b 100644 --- a/src/main/kotlin/app/extractors/Extractor.kt +++ b/src/main/kotlin/app/extractors/Extractor.kt @@ -28,6 +28,7 @@ class Extractor : ExtractorInterface { in GoExtractor.FILE_EXTS -> GoExtractor() in ObjectiveCExtractor.FILE_EXTS -> ObjectiveCExtractor() in SwiftExtractor.FILE_EXTS -> SwiftExtractor() + in KotlinExtractor.FILE_EXTS -> KotlinExtractor() else -> CommonExtractor() } } diff --git a/src/main/kotlin/app/extractors/JavaExtractor.kt b/src/main/kotlin/app/extractors/JavaExtractor.kt index 5b990421..c30854a3 100644 --- a/src/main/kotlin/app/extractors/JavaExtractor.kt +++ b/src/main/kotlin/app/extractors/JavaExtractor.kt @@ -79,8 +79,10 @@ class JavaExtractor : ExtractorInterface { override fun tokenize(line: String): List { val importRegex = Regex("""^(.*import)\s[^\n]*""") val commentRegex = Regex("""^([^\n]*//)[^\n]*""") + val packageRegex = Regex("""^(.*package)\s[^\n]*""") var newLine = importRegex.replace(line, "") newLine = commentRegex.replace(newLine, "") + newLine = packageRegex.replace(newLine, "") return super.tokenize(newLine) } diff --git a/src/main/kotlin/app/extractors/KotlinExtractor.kt b/src/main/kotlin/app/extractors/KotlinExtractor.kt new file mode 100644 index 00000000..f7ac37ff --- /dev/null +++ b/src/main/kotlin/app/extractors/KotlinExtractor.kt @@ -0,0 +1,58 @@ +// Copyright 2017 Sourcerer Inc. All Rights Reserved. +// Author: Liubov Yaronskaya (lyaronskaya@sourcerer.io) + +package app.extractors + +import app.model.CommitStats +import app.model.DiffFile + +class KotlinExtractor : ExtractorInterface { + companion object { + val LANGUAGE_NAME = "kotlin" + val FILE_EXTS = listOf("kt") + val LIBRARIES = ExtractorInterface.getLibraries(LANGUAGE_NAME) + val evaluator by lazy { + ExtractorInterface.getLibraryClassifier(LANGUAGE_NAME) + } + } + + override fun extract(files: List): List { + files.map { file -> file.language = LANGUAGE_NAME } + return super.extract(files) + } + + override fun extractImports(fileContent: List): List { + val imports = mutableSetOf() + + val regex = Regex("""import\s+(\w+[.\w+]*)""") + fileContent.forEach { + val res = regex.find(it) + if (res != null) { + val importedName = res.groupValues[1] + LIBRARIES.forEach { library -> + if (importedName.startsWith(library)) { + imports.add(library) + } + } + } + } + + return imports.toList() + } + + override fun tokenize(line: String): List { + val importRegex = Regex("""^(.*import)\s[^\n]*""") + val commentRegex = Regex("""^([^\n]*//)[^\n]*""") + val packageRegex = Regex("""^(.*package)\s[^\n]*""") + var newLine = importRegex.replace(line, "") + newLine = commentRegex.replace(newLine, "") + newLine = packageRegex.replace(newLine, "") + return super.tokenize(newLine) + } + + override fun getLineLibraries(line: String, + fileLibraries: List): List { + + return super.getLineLibraries(line, fileLibraries, evaluator, LANGUAGE_NAME) + } +} diff --git a/src/main/kotlin/app/hashers/CodeLongevity.kt b/src/main/kotlin/app/hashers/CodeLongevity.kt index 1a8dea98..90dfc833 100644 --- a/src/main/kotlin/app/hashers/CodeLongevity.kt +++ b/src/main/kotlin/app/hashers/CodeLongevity.kt @@ -151,7 +151,7 @@ class CodeLongevity(private val serverRepo: Repo, catch(e: Exception) { throw Exception("No branch") } val df = DiffFormatter(DisabledOutputStream.INSTANCE) - val dataPath = FileHelper.getPath(serverRepo.rehash, "longevity") + val dataPath = FileHelper.toPath(serverRepo.rehash, "longevity") init { df.setRepository(repo) diff --git a/src/main/kotlin/app/hashers/RepoHasher.kt b/src/main/kotlin/app/hashers/RepoHasher.kt index 80e00c96..d1fabb28 100644 --- a/src/main/kotlin/app/hashers/RepoHasher.kt +++ b/src/main/kotlin/app/hashers/RepoHasher.kt @@ -9,6 +9,7 @@ import app.config.Configurator import app.model.Author import app.model.LocalRepo import app.model.Repo +import app.utils.FileHelper.toPath import app.utils.HashingException import app.utils.RepoHelper import org.eclipse.jgit.api.Git @@ -17,11 +18,11 @@ import java.io.IOException import kotlin.collections.HashSet class RepoHasher(private val localRepo: LocalRepo, private val api: Api, - private val configurator: Configurator) { + private val configurator: Configurator) { var serverRepo: Repo = Repo() init { - if (!RepoHelper.isValidRepo(localRepo.path)) { + if (!RepoHelper.isValidRepo(localRepo.path.toPath())) { throw IllegalArgumentException("Invalid repo $localRepo") } } diff --git a/src/main/kotlin/app/ui/AddRepoState.kt b/src/main/kotlin/app/ui/AddRepoState.kt index 1ebc3cee..cb5e42f5 100644 --- a/src/main/kotlin/app/ui/AddRepoState.kt +++ b/src/main/kotlin/app/ui/AddRepoState.kt @@ -7,6 +7,7 @@ import app.Logger import app.api.Api import app.config.Configurator import app.model.LocalRepo +import app.utils.FileHelper.toPath import app.utils.RepoHelper import app.utils.UiHelper @@ -28,12 +29,13 @@ class AddRepoState constructor(private val context: Context, if (configurator.getLocalRepos().isEmpty()) { Logger.print("Add at least one valid repository.") } else { - break // User finished to add repos. + break // User finished to add repos. } } else { - if (RepoHelper.isValidRepo(pathString)) { - Logger.print("Added git repository at $pathString.") - val localRepo = LocalRepo(pathString) + val path = pathString.toPath() + if (RepoHelper.isValidRepo(path)) { + Logger.print("Added git repository at $path.") + val localRepo = LocalRepo(path.toString()) localRepo.hashAllContributors = UiHelper.confirm("Do you " + "want to hash commits of all contributors?", defaultIsYes = true) diff --git a/src/main/kotlin/app/utils/FileHelper.kt b/src/main/kotlin/app/utils/FileHelper.kt index 668348f1..7950808d 100644 --- a/src/main/kotlin/app/utils/FileHelper.kt +++ b/src/main/kotlin/app/utils/FileHelper.kt @@ -3,6 +3,7 @@ package app.utils +import app.Logger import java.io.File import java.net.URLDecoder import java.nio.file.Files @@ -17,7 +18,7 @@ object FileHelper { private val jarPath = getJarPath() private val settingsPath = jarPath.resolve(dirName) - fun getPath(name: String, vararg parts: String): Path { + fun toPath(name: String, vararg parts: String): Path { val path = settingsPath.resolve(Paths.get("", *parts)) if (Files.notExists(path)) { Files.createDirectories(path) @@ -26,11 +27,11 @@ object FileHelper { } fun getFile(name: String, vararg parts: String): File { - return getPath(name, *parts).toFile() + return toPath(name, *parts).toFile() } fun notExists(name:String, vararg parts: String): Boolean { - return Files.notExists(getPath(name, *parts)) + return Files.notExists(toPath(name, *parts)) } fun getFileExtension(path: String): String { @@ -47,4 +48,13 @@ object FileHelper { // Removing jar filename. return root.resolve(fullPath.subpath(0, fullPath.nameCount - 1)) } + + fun String.toPath(): Path { + val substitutePath = if (this.startsWith("~" + File.separator)) { + System.getProperty("user.home") + this.substring(1) + } else { this } + val pathTemp = Paths.get(substitutePath).toAbsolutePath().normalize() + println(pathTemp.toString()) + return pathTemp + } } diff --git a/src/main/kotlin/app/utils/RepoHelper.kt b/src/main/kotlin/app/utils/RepoHelper.kt index 9151423e..9d6dccaf 100644 --- a/src/main/kotlin/app/utils/RepoHelper.kt +++ b/src/main/kotlin/app/utils/RepoHelper.kt @@ -12,13 +12,14 @@ import org.eclipse.jgit.lib.ObjectId import org.eclipse.jgit.lib.Repository import java.io.File import java.nio.file.InvalidPathException +import java.nio.file.Path import java.nio.file.Paths /** * Class for utility functions on repos. */ object RepoHelper { - fun isValidRepo(path: String): Boolean { + fun isValidRepo(path: Path): Boolean { if (!isDirectory(path)) { return false } @@ -27,7 +28,7 @@ object RepoHelper { var repository: Repository? = null val commitId: ObjectId? try { - git = Git.open(File(path)) + git = Git.open(path.toFile()) repository = git.repository commitId = CommitCrawler.getDefaultBranchHead(git) } catch (e: Exception) { @@ -44,9 +45,9 @@ object RepoHelper { return false } - fun isDirectory(path: String): Boolean { + fun isDirectory(path: Path): Boolean { return try { - Paths.get(path).toFile().isDirectory + path.toFile().isDirectory } catch (e: InvalidPathException) { Logger.error(e, "Invalid path") false diff --git a/src/main/resources/data/libraries/kotlin_libraries.txt b/src/main/resources/data/libraries/kotlin_libraries.txt new file mode 100644 index 00000000..d2ea8dcc --- /dev/null +++ b/src/main/resources/data/libraries/kotlin_libraries.txt @@ -0,0 +1,56 @@ +com.winterbe.expekt +com.github.vassilibykov.adventkt +me.lazmaid.kraph +com.github.kittinunf.fuel +imgui +org.mapdb +uy.kohesive.kovert +io.reactivex.rxkotlin +com.compass.snail +io.kweb +com.github.andrewoma.kwery +com.github.pgutkowski.kgraphql +io.vertx +org.spekframework.spek2 +com.oneeyedmen.konsent +com.github.salomonbrys.kotson +org.jire.kton +io.kotlintest +ktx +org.kotlinprimavera +org.kottpd +com.almasb.fxgl +org.wasabifx.wasabi +com.fboldog.ext4klaxon +net.yested +ua.com.lavi.komock +kotlinx.nosql +spark +com.github.kittinunf.result +com.nivabit.kuery +io.thelandscape.krawler +io.tekniq +com.beust.klaxon +com.github.shyiko.levelkt +io.javalin +kategory +tornadofx +kotliquery +khttp +ovr +com.hexagonkt +com.nhaarman.mockito_kotlin +io.ktor +com.codepoetics.klenses +io.polymorphicpanda.kspec +com.natpryce.hamkrest +org.http4k +com.vaadin +io.mockk +kara +org.funktionale +com.github.fluidsonic.fluid.json +com.squareup.sqldelight +org.amshove.kluent +com.danneu.kog +org.jetbrains.exposed \ No newline at end of file diff --git a/src/test/kotlin/test/tests/extractors/ExtractorTest.kt b/src/test/kotlin/test/tests/extractors/ExtractorTest.kt index dbe26f4b..36ef1e00 100644 --- a/src/test/kotlin/test/tests/extractors/ExtractorTest.kt +++ b/src/test/kotlin/test/tests/extractors/ExtractorTest.kt @@ -104,6 +104,12 @@ class ExtractorTest : Spek({ assertExtractsLineLibraries("grpc", line, CExtractor()) } + + it("kotlin extractor extracts the library") { + val line = "FuelManager.instance.apply {" + assertExtractsLineLibraries("com.github.kittinunf.fuel", + line, KotlinExtractor()) + } } given("code line doesn't use libraries" ) { @@ -161,6 +167,11 @@ class ExtractorTest : Spek({ val line = "int main(int argc, char **argv) {" assertExtractsNoLibraries(line, CExtractor()) } + + it("kotlin extractor returns empty list") { + val line = "val password = \"P@\$\\\$vv0|2|)\"" + assertExtractsNoLibraries(line, KotlinExtractor()) + } } given("import name.h") { @@ -179,6 +190,14 @@ class ExtractorTest : Spek({ } } + given("line contains import") { + it("kotlin extractor extracts import") { + val line = "import kategory.optics.*" + val lib = "kategory" + assertExtractsImport(lib, line, KotlinExtractor()) + } + } + given("import cv2 or cv") { it("imports opencv") { val lib = "opencv" diff --git a/src/test/kotlin/test/tests/utils/FileHelperTest.kt b/src/test/kotlin/test/tests/utils/FileHelperTest.kt new file mode 100644 index 00000000..9536dce2 --- /dev/null +++ b/src/test/kotlin/test/tests/utils/FileHelperTest.kt @@ -0,0 +1,27 @@ +// Copyright 2017 Sourcerer Inc. All Rights Reserved. +// Author: Ryan Osilla (ryan@sourcerer.io) + +package test.tests.utils + +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import kotlin.test.assertEquals +import app.utils.FileHelper.toPath +import java.nio.file.Paths + +fun testPath(expectedPath: String, actualPath: String) { + assertEquals(Paths.get(expectedPath), actualPath.toPath()) +} + +class FileHelperTest : Spek({ + given("relative path test") { + it("basic") { + val home = System.getProperty("user.home") + testPath("/Users/user/repo", "/Users/user/repo") + testPath("/Users/user/repo", "/Users/user/../user/repo/../repo") + testPath("$home/test", "~/test") + testPath("$home/test1", "~/test/../test1") + } + } +})