diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c23a9c42..ce922ac82c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/) - Fix file extensions of output files for merged projects [#3421](https://github.com/MaibornWolff/codecharta/pull/3421) - Fix the ability for users to accidentally pass invalid metrics to the RawTextParser without it crashing [#3424](https://github.com/MaibornWolff/codecharta/pull/3424) - Fix deselected buildings with green/red roof in delta mode do not reset their color roof [#3426](https://github.com/MaibornWolff/codecharta/pull/3426) +- Fix parser hang issue in interactive mode caused by unintentional "enter" input after the last question [#3422](https://github.com/MaibornWolff/codecharta/pull/3422) ### Chore ‍👨‍💻 👩‍💻 diff --git a/analysis/export/CSVExporter/src/main/kotlin/de/maibornwolff/codecharta/exporter/csv/CSVExporter.kt b/analysis/export/CSVExporter/src/main/kotlin/de/maibornwolff/codecharta/exporter/csv/CSVExporter.kt index 2d2e569114..1ca806dc0f 100644 --- a/analysis/export/CSVExporter/src/main/kotlin/de/maibornwolff/codecharta/exporter/csv/CSVExporter.kt +++ b/analysis/export/CSVExporter/src/main/kotlin/de/maibornwolff/codecharta/exporter/csv/CSVExporter.kt @@ -27,7 +27,7 @@ import java.util.concurrent.Callable description = [CSVExporter.DESCRIPTION], footer = [CodeChartaConstants.General.GENERIC_FOOTER] ) -class CSVExporter() : Callable, InteractiveParser { +class CSVExporter() : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) private var help = false @@ -65,7 +65,7 @@ class CSVExporter() : Callable, InteractiveParser { } @Throws(IOException::class) - override fun call(): Void? { + override fun call(): Unit? { if (maxHierarchy < 0) { throw IllegalArgumentException("depth-of-hierarchy must not be negative") } diff --git a/analysis/filter/EdgeFilter/src/main/kotlin/de/maibornwolff/codecharta/filter/edgefilter/EdgeFilter.kt b/analysis/filter/EdgeFilter/src/main/kotlin/de/maibornwolff/codecharta/filter/edgefilter/EdgeFilter.kt index 1f6c7b1d28..9d76f8968a 100644 --- a/analysis/filter/EdgeFilter/src/main/kotlin/de/maibornwolff/codecharta/filter/edgefilter/EdgeFilter.kt +++ b/analysis/filter/EdgeFilter/src/main/kotlin/de/maibornwolff/codecharta/filter/edgefilter/EdgeFilter.kt @@ -18,7 +18,7 @@ import java.util.concurrent.Callable ) class EdgeFilter( private val output: PrintStream = System.out -) : Callable, InteractiveParser { +) : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) var help: Boolean = false @@ -45,7 +45,7 @@ class EdgeFilter( } } - override fun call(): Void? { + override fun call(): Unit? { if (!InputHelper.isInputValidAndNotNull(arrayOf(source), canInputContainFolders = false)) { throw IllegalArgumentException("Input invalid file for EdgeFilter, stopping execution...") } diff --git a/analysis/filter/MergeFilter/src/main/kotlin/de/maibornwolff/codecharta/filter/mergefilter/MergeFilter.kt b/analysis/filter/MergeFilter/src/main/kotlin/de/maibornwolff/codecharta/filter/mergefilter/MergeFilter.kt index 984b5e262f..9c7b44aadd 100644 --- a/analysis/filter/MergeFilter/src/main/kotlin/de/maibornwolff/codecharta/filter/mergefilter/MergeFilter.kt +++ b/analysis/filter/MergeFilter/src/main/kotlin/de/maibornwolff/codecharta/filter/mergefilter/MergeFilter.kt @@ -20,7 +20,7 @@ import java.util.concurrent.Callable ) class MergeFilter( private val output: PrintStream = System.out -) : Callable, InteractiveParser { +) : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) var help: Boolean = false @@ -68,7 +68,7 @@ class MergeFilter( } } - override fun call(): Void? { + override fun call(): Unit? { val nodeMergerStrategy = when { leafStrategySet -> LeafNodeMergerStrategy(addMissingNodes, ignoreCase) diff --git a/analysis/filter/StructureModifier/src/main/kotlin/de/maibornwolff/codecharta/filter/structuremodifier/StructureModifier.kt b/analysis/filter/StructureModifier/src/main/kotlin/de/maibornwolff/codecharta/filter/structuremodifier/StructureModifier.kt index 848a925ecb..3c55136a77 100644 --- a/analysis/filter/StructureModifier/src/main/kotlin/de/maibornwolff/codecharta/filter/structuremodifier/StructureModifier.kt +++ b/analysis/filter/StructureModifier/src/main/kotlin/de/maibornwolff/codecharta/filter/structuremodifier/StructureModifier.kt @@ -23,7 +23,7 @@ class StructureModifier( private val input: InputStream = System.`in`, private val output: PrintStream = System.out, private val error: PrintStream = System.err -) : Callable, InteractiveParser { +) : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) var help: Boolean = false @@ -69,7 +69,7 @@ class StructureModifier( } } - override fun call(): Void? { + override fun call(): Unit? { project = readProject() ?: return null diff --git a/analysis/filter/StructureModifier/src/test/kotlin/de/maibornwolff/codecharta/filter/structuremodifier/StructureModifierTest.kt b/analysis/filter/StructureModifier/src/test/kotlin/de/maibornwolff/codecharta/filter/structuremodifier/StructureModifierTest.kt index 5c79b05672..36075f50b6 100644 --- a/analysis/filter/StructureModifier/src/test/kotlin/de/maibornwolff/codecharta/filter/structuremodifier/StructureModifierTest.kt +++ b/analysis/filter/StructureModifier/src/test/kotlin/de/maibornwolff/codecharta/filter/structuremodifier/StructureModifierTest.kt @@ -33,8 +33,8 @@ class StructureModifierTest { @Test fun `reads project piped input`() { - val input = File("src/test/resources/sample_project.cc.json").bufferedReader().readLines() - .joinToString(separator = "") { it } + val inputFilePath = "src/test/resources/sample_project.cc.json" + val input = File(inputFilePath).bufferedReader().readLines().joinToString(separator = "") { it } val cliResult = executeForOutput(input, arrayOf("-r=/does/not/exist")) diff --git a/analysis/import/CSVImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/csv/CSVImporter.kt b/analysis/import/CSVImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/csv/CSVImporter.kt index b053c6e1df..1db1118bd3 100644 --- a/analysis/import/CSVImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/csv/CSVImporter.kt +++ b/analysis/import/CSVImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/csv/CSVImporter.kt @@ -19,7 +19,7 @@ import java.util.concurrent.Callable ) class CSVImporter( private val output: PrintStream = System.out -) : Callable, InteractiveParser { +) : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) private var help = false @@ -56,7 +56,7 @@ class CSVImporter( } @Throws(IOException::class) - override fun call(): Void? { + override fun call(): Unit? { if (!InputHelper.isInputValid(files.toTypedArray(), canInputContainFolders = false)) { throw IllegalArgumentException("Input invalid file for CSVImporter, stopping execution...") } diff --git a/analysis/import/CSVImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/sourcemonitor/SourceMonitorImporter.kt b/analysis/import/CSVImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/sourcemonitor/SourceMonitorImporter.kt index 8a83eb3fbf..da90a83c5c 100644 --- a/analysis/import/CSVImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/sourcemonitor/SourceMonitorImporter.kt +++ b/analysis/import/CSVImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/sourcemonitor/SourceMonitorImporter.kt @@ -21,7 +21,7 @@ import java.util.concurrent.Callable ) class SourceMonitorImporter( private val output: PrintStream = System.out -) : Callable, InteractiveParser { +) : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) private var help = false @@ -53,7 +53,7 @@ class SourceMonitorImporter( } @Throws(IOException::class) - override fun call(): Void? { + override fun call(): Unit? { if (!InputHelper.isInputValid(files.toTypedArray(), canInputContainFolders = false)) { throw IllegalArgumentException("Input invalid file for SourceMonitorImporter, stopping execution...") } diff --git a/analysis/import/CodeMaatImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/codemaat/CodeMaatImporter.kt b/analysis/import/CodeMaatImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/codemaat/CodeMaatImporter.kt index fc53d4bc40..0adb93e1e7 100644 --- a/analysis/import/CodeMaatImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/codemaat/CodeMaatImporter.kt +++ b/analysis/import/CodeMaatImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/codemaat/CodeMaatImporter.kt @@ -21,7 +21,7 @@ import java.util.concurrent.Callable footer = [CodeChartaConstants.General.GENERIC_FOOTER] ) class CodeMaatImporter( - private val output: PrintStream = System.out) : Callable, InteractiveParser { + private val output: PrintStream = System.out) : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) private var help = false @@ -53,7 +53,7 @@ class CodeMaatImporter( } @Throws(IOException::class) - override fun call(): Void? { + override fun call(): Unit? { if (!InputHelper.isInputValid(files.toTypedArray(), canInputContainFolders = false)) { throw IllegalArgumentException("Input invalid file for CodeMaatImporter, stopping execution...") } diff --git a/analysis/import/GitLogParser/build.gradle b/analysis/import/GitLogParser/build.gradle index d02d7c06c0..a8b275ebaa 100644 --- a/analysis/import/GitLogParser/build.gradle +++ b/analysis/import/GitLogParser/build.gradle @@ -2,6 +2,7 @@ dependencies { implementation project(':model') implementation project(':filter:MergeFilter') implementation project(':tools:InteractiveParser') + implementation project(':tools:PipeableParser') implementation group: 'org.apache.commons', name: 'commons-lang3', version: commons_lang3_version implementation group: 'info.picocli', name: 'picocli', version: picocli_version diff --git a/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/GitLogParser.kt b/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/GitLogParser.kt index adf212a2b6..86c0851f56 100644 --- a/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/GitLogParser.kt +++ b/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/GitLogParser.kt @@ -14,6 +14,8 @@ import de.maibornwolff.codecharta.serialization.ProjectSerializer import de.maibornwolff.codecharta.tools.interactiveparser.InteractiveParser import de.maibornwolff.codecharta.tools.interactiveparser.ParserDialogInterface import de.maibornwolff.codecharta.tools.interactiveparser.util.CodeChartaConstants +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParser +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParserSyncFlag import de.maibornwolff.codecharta.util.ResourceSearchHelper import org.mozilla.universalchardet.UniversalDetector import picocli.CommandLine @@ -36,7 +38,7 @@ class GitLogParser( private val input: InputStream = System.`in`, private val output: PrintStream = System.out, private val error: PrintStream = System.err -) : Callable, InteractiveParser { +) : Callable, InteractiveParser, PipeableParser { private val inputFormatNames = GIT_LOG_NUMSTAT_RAW_REVERSED @@ -94,8 +96,8 @@ class GitLogParser( } @Throws(IOException::class) - override fun call(): Void? { - print(" ") + override fun call(): Unit? { + logPipeableParserSyncSignal(PipeableParserSyncFlag.SYNC_FLAG) return null } diff --git a/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/subcommands/LogScanCommand.kt b/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/subcommands/LogScanCommand.kt index ca47678ff3..a3c595e2b3 100644 --- a/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/subcommands/LogScanCommand.kt +++ b/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/subcommands/LogScanCommand.kt @@ -15,7 +15,7 @@ import java.util.concurrent.Callable footer = [CodeChartaConstants.General.GENERIC_FOOTER] ) -class LogScanCommand : Callable, InteractiveParser { +class LogScanCommand : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) private var help = false @@ -62,7 +62,7 @@ class LogScanCommand : Callable, InteractiveParser { const val DESCRIPTION = "git log parser log-scan - generates cc.json from a given git-log file" } - override fun call(): Void? { + override fun call(): Unit? { if (!InputHelper.isInputValidAndNotNull(arrayOf(gitLogFile), canInputContainFolders = false)) { throw IllegalArgumentException("Input invalid file for GitLogScan, stopping execution...") } diff --git a/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/subcommands/RepoScanCommand.kt b/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/subcommands/RepoScanCommand.kt index 58e98f2797..a2887b1a51 100644 --- a/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/subcommands/RepoScanCommand.kt +++ b/analysis/import/GitLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/gitlogparser/subcommands/RepoScanCommand.kt @@ -17,7 +17,7 @@ import java.util.concurrent.Callable footer = ["Copyright(c) 2022, MaibornWolff GmbH"] ) -class RepoScanCommand : Callable, InteractiveParser { +class RepoScanCommand : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) private var help = false @@ -53,7 +53,7 @@ class RepoScanCommand : Callable, InteractiveParser { const val DESCRIPTION = "git log parser repo-scan - generates cc.json from an automatically generated git-log file" } - override fun call(): Void? { + override fun call(): Unit? { val repoPath: Path if (repoPathName == null || !InputHelper.isInputValid(arrayOf(File(repoPathName!!)), canInputContainFolders = true)) { throw IllegalArgumentException("Input invalid file for GitRepoScan, stopping execution...") diff --git a/analysis/import/MetricGardenerImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/metricgardenerimporter/MetricGardenerImporter.kt b/analysis/import/MetricGardenerImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/metricgardenerimporter/MetricGardenerImporter.kt index 8194797fdf..dfb223ed49 100644 --- a/analysis/import/MetricGardenerImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/metricgardenerimporter/MetricGardenerImporter.kt +++ b/analysis/import/MetricGardenerImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/metricgardenerimporter/MetricGardenerImporter.kt @@ -25,7 +25,7 @@ import java.util.concurrent.Callable class MetricGardenerImporter( private val output: PrintStream = System.out -) : Callable, InteractiveParser { +) : Callable, InteractiveParser { private val mapper = jacksonObjectMapper() @@ -71,7 +71,7 @@ class MetricGardenerImporter( } @Throws(IOException::class) - override fun call(): Void? { + override fun call(): Unit? { if (!InputHelper.isInputValidAndNotNull(arrayOf(inputFile), canInputContainFolders = true)) { throw IllegalArgumentException("Input invalid file for MetricGardenerImporter, stopping execution...") } diff --git a/analysis/import/SVNLogParser/build.gradle b/analysis/import/SVNLogParser/build.gradle index 1988bc9044..f7be31ec6e 100644 --- a/analysis/import/SVNLogParser/build.gradle +++ b/analysis/import/SVNLogParser/build.gradle @@ -2,6 +2,7 @@ dependencies { implementation project(':model') implementation project(':filter:MergeFilter') implementation project(':tools:InteractiveParser') + implementation project(':tools:PipeableParser') implementation group: 'org.apache.commons', name: 'commons-lang3', version: commons_lang3_version implementation group: 'info.picocli', name: 'picocli', version: picocli_version diff --git a/analysis/import/SVNLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/svnlogparser/SVNLogParser.kt b/analysis/import/SVNLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/svnlogparser/SVNLogParser.kt index f917e17a2b..79fe57daf3 100644 --- a/analysis/import/SVNLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/svnlogparser/SVNLogParser.kt +++ b/analysis/import/SVNLogParser/src/main/kotlin/de/maibornwolff/codecharta/importer/svnlogparser/SVNLogParser.kt @@ -12,6 +12,8 @@ import de.maibornwolff.codecharta.serialization.ProjectSerializer import de.maibornwolff.codecharta.tools.interactiveparser.InteractiveParser import de.maibornwolff.codecharta.tools.interactiveparser.ParserDialogInterface import de.maibornwolff.codecharta.tools.interactiveparser.util.CodeChartaConstants +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParser +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParserSyncFlag import de.maibornwolff.codecharta.util.InputHelper import de.maibornwolff.codecharta.util.ResourceSearchHelper import org.mozilla.universalchardet.UniversalDetector @@ -22,7 +24,6 @@ import java.io.InputStream import java.io.PrintStream import java.nio.charset.Charset import java.nio.file.Files -import java.util.Arrays import java.util.concurrent.Callable import java.util.stream.Stream @@ -35,7 +36,7 @@ class SVNLogParser( private val input: InputStream = System.`in`, private val output: PrintStream = System.out, private val error: PrintStream = System.err -) : Callable, InteractiveParser { +) : Callable, InteractiveParser, PipeableParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) private var help = false @@ -64,7 +65,7 @@ class SVNLogParser( private val metricsFactory: MetricsFactory get() { - val nonChurnMetrics = Arrays.asList( + val nonChurnMetrics = listOf( "age_in_weeks", "number_of_authors", "number_of_commits", @@ -107,8 +108,8 @@ class SVNLogParser( } @Throws(IOException::class) - override fun call(): Void? { - print(" ") + override fun call(): Unit? { + logPipeableParserSyncSignal(PipeableParserSyncFlag.SYNC_FLAG) if (!InputHelper.isInputValidAndNotNull(arrayOf(file), canInputContainFolders = false)) { throw IllegalArgumentException("Input invalid file for SVNLogParser, stopping execution...") diff --git a/analysis/import/SonarImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/sonar/SonarImporterMain.kt b/analysis/import/SonarImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/sonar/SonarImporterMain.kt index 5d0ec89728..dea77c29a6 100644 --- a/analysis/import/SonarImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/sonar/SonarImporterMain.kt +++ b/analysis/import/SonarImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/sonar/SonarImporterMain.kt @@ -25,7 +25,7 @@ import java.util.concurrent.Callable class SonarImporterMain( private val input: InputStream = System.`in`, private val output: PrintStream = System.out -) : Callable, InteractiveParser { +) : Callable, InteractiveParser { @CommandLine.Option( names = ["-h", "--help"], usageHelp = true, description = [ @@ -88,7 +88,7 @@ class SonarImporterMain( return SonarMeasuresAPIImporter(measuresDatasource, metricsDatasource, sonarCodeURLLinker, translator, usePath) } - override fun call(): Void? { + override fun call(): Unit? { if (url == "" || projectId == "") { throw IllegalArgumentException("Input invalid Url or ProjectID for SonarImporter, stopping execution...") } diff --git a/analysis/import/SourceCodeParser/build.gradle b/analysis/import/SourceCodeParser/build.gradle index 1e856629a9..df407b9039 100644 --- a/analysis/import/SourceCodeParser/build.gradle +++ b/analysis/import/SourceCodeParser/build.gradle @@ -2,6 +2,7 @@ dependencies { implementation project(':model') implementation project(':filter:MergeFilter') implementation project(':tools:InteractiveParser') + implementation project(':tools:PipeableParser') implementation group: 'info.picocli', name: 'picocli', version: picocli_version diff --git a/analysis/import/SourceCodeParser/src/main/kotlin/de/maibornwolff/codecharta/importer/sourcecodeparser/SourceCodeParserMain.kt b/analysis/import/SourceCodeParser/src/main/kotlin/de/maibornwolff/codecharta/importer/sourcecodeparser/SourceCodeParserMain.kt index b990344e4b..4ebd7c056e 100644 --- a/analysis/import/SourceCodeParser/src/main/kotlin/de/maibornwolff/codecharta/importer/sourcecodeparser/SourceCodeParserMain.kt +++ b/analysis/import/SourceCodeParser/src/main/kotlin/de/maibornwolff/codecharta/importer/sourcecodeparser/SourceCodeParserMain.kt @@ -9,6 +9,8 @@ import de.maibornwolff.codecharta.serialization.ProjectDeserializer import de.maibornwolff.codecharta.tools.interactiveparser.InteractiveParser import de.maibornwolff.codecharta.tools.interactiveparser.ParserDialogInterface import de.maibornwolff.codecharta.tools.interactiveparser.util.CodeChartaConstants +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParser +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParserSyncFlag import de.maibornwolff.codecharta.util.InputHelper import de.maibornwolff.codecharta.util.ResourceSearchHelper import mu.KotlinLogging @@ -30,10 +32,10 @@ import java.util.concurrent.Callable footer = [SourceCodeParserMain.FOOTER] ) class SourceCodeParserMain( - private val outputStream: PrintStream, - private val input: InputStream = System.`in`, - private val error: PrintStream = System.err -) : Callable, InteractiveParser { + private val output: PrintStream, + private val input: InputStream = System.`in`, + private val error: PrintStream = System.err +) : Callable, InteractiveParser, PipeableParser { // we need this constructor because ccsh requires an empty constructor constructor() : this(System.out) @@ -103,8 +105,9 @@ class SourceCodeParserMain( } @Throws(IOException::class) - override fun call(): Void? { - print(" ") + override fun call(): Unit? { + logPipeableParserSyncSignal(PipeableParserSyncFlag.SYNC_FLAG) + if (!InputHelper.isInputValidAndNotNull(arrayOf(file), canInputContainFolders = true)) { throw IllegalArgumentException("Input invalid file for SourceCodeParser, stopping execution...") } @@ -134,7 +137,7 @@ class SourceCodeParserMain( private fun getCsvOutputWriter(): Writer { return if (outputFile == null) { - OutputStreamWriter(outputStream) + OutputStreamWriter(output) } else { val outputName = outputFile!!.name BufferedWriter(FileWriter(OutputFileHandler.checkAndFixFileExtension(outputName, false, FileExtension.CSV))) @@ -152,7 +155,7 @@ class SourceCodeParserMain( } } - private fun getJsonOutputStream() = OutputFileHandler.stream(outputFile?.absolutePath, outputStream, compress) + private fun getJsonOutputStream() = OutputFileHandler.stream(outputFile?.absolutePath, output, compress) override fun getDialog(): ParserDialogInterface = ParserDialog override fun isApplicable(resourceToBeParsed: String): Boolean { diff --git a/analysis/import/TokeiImporter/build.gradle b/analysis/import/TokeiImporter/build.gradle index 5e42d06665..a4f3060376 100644 --- a/analysis/import/TokeiImporter/build.gradle +++ b/analysis/import/TokeiImporter/build.gradle @@ -1,6 +1,7 @@ dependencies { implementation project(':model') implementation project(':tools:InteractiveParser') + implementation project(':tools:PipeableParser') implementation group: 'info.picocli', name: 'picocli', version: picocli_version implementation group: 'io.fastjson', name: 'boon', version: boon_version diff --git a/analysis/import/TokeiImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/tokeiimporter/TokeiImporter.kt b/analysis/import/TokeiImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/tokeiimporter/TokeiImporter.kt index 12fe28a8fe..bb97bd3766 100644 --- a/analysis/import/TokeiImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/tokeiimporter/TokeiImporter.kt +++ b/analysis/import/TokeiImporter/src/main/kotlin/de/maibornwolff/codecharta/importer/tokeiimporter/TokeiImporter.kt @@ -8,11 +8,13 @@ import de.maibornwolff.codecharta.importer.tokeiimporter.strategy.TokeiTwelveStr import de.maibornwolff.codecharta.model.AttributeType import de.maibornwolff.codecharta.model.AttributeTypes import de.maibornwolff.codecharta.model.ProjectBuilder +import de.maibornwolff.codecharta.serialization.ProjectInputReader import de.maibornwolff.codecharta.serialization.ProjectSerializer -import de.maibornwolff.codecharta.serialization.mapLines import de.maibornwolff.codecharta.tools.interactiveparser.InteractiveParser import de.maibornwolff.codecharta.tools.interactiveparser.ParserDialogInterface import de.maibornwolff.codecharta.tools.interactiveparser.util.CodeChartaConstants +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParser +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParserSyncFlag import de.maibornwolff.codecharta.util.InputHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -35,7 +37,7 @@ class TokeiImporter( private val input: InputStream = System.`in`, private val output: PrintStream = System.out, private val error: PrintStream = System.err -) : Callable, InteractiveParser { +) : Callable, InteractiveParser, PipeableParser { private val logger = KotlinLogging.logger {} @@ -84,8 +86,9 @@ class TokeiImporter( } @Throws(IOException::class) - override fun call(): Void? { - print(" ") + override fun call(): Unit? { + logPipeableParserSyncSignal(PipeableParserSyncFlag.SYNC_FLAG) + projectBuilder = ProjectBuilder() val root = getInput() ?: return null runBlocking(Dispatchers.Default) { @@ -125,7 +128,7 @@ class TokeiImporter( } } else { launch { - val projectString: String = input.mapLines { it }.joinToString(separator = "") { it } + val projectString: String = ProjectInputReader.extractProjectString(input) if (projectString.isNotEmpty()) { root = JsonParser.parseString(projectString) } else { diff --git a/analysis/model/build.gradle b/analysis/model/build.gradle index 3763de5832..b656fc58fe 100644 --- a/analysis/model/build.gradle +++ b/analysis/model/build.gradle @@ -3,6 +3,7 @@ dependencies { implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlin_logging_version implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version implementation group: 'io.github.microutils', name: 'kotlin-logging-jvm', version: '2.1.23' + implementation project(':tools:PipeableParser') implementation group: 'info.picocli', name: 'picocli', version: picocli_version implementation group: 'org.slf4j', name: 'slf4j-simple', version: slf4j_version @@ -12,6 +13,7 @@ dependencies { testImplementation group: 'org.assertj', name: 'assertj-core', version: assertj_version testImplementation group: 'io.mockk', name: 'mockk', version: mockk_version testImplementation group: 'org.skyscreamer', name: 'jsonassert', version: jsonassert_version + testImplementation project(':tools:PipeableParser') testRuntimeOnly group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version } diff --git a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/PipedInputScanner.kt b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/PipedInputScanner.kt deleted file mode 100644 index 55f00a06d4..0000000000 --- a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/PipedInputScanner.kt +++ /dev/null @@ -1,28 +0,0 @@ -package de.maibornwolff.codecharta.serialization - -import java.io.InputStream -import java.util.Scanner - -fun InputStream.mapLines(transform: (String) -> R): List { - val result = mutableListOf() - forEachLine { - result.add(transform(it)) - } - return result -} - -// Bash runs commands in a pipe chain in parallel. This means that the input may not be available/complete at the -// time of reading it. -// The ccsh commands need to determine whether there is piped input or not. If there is, it should wait until the -// input is complete. To indicate that there will be a piped project, the filters/importers of ccsh send a blank to -// their OutputStream as soon as they start. This is detected by potentially following commands and is taken as a -// sign to wait. In order to give the preceding command time to send this blank, we wait for some time before -// checking the availability of the InputStream. -fun InputStream.forEachLine(action: (String) -> Unit) { - val scanner = Scanner(this) - Thread.sleep(1000) - if (available() <= 0) return // Otherwise it will get stuck waiting for user input - while (scanner.hasNext()) { - action(scanner.nextLine()) - } -} diff --git a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectDeserializer.kt b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectDeserializer.kt index 9880451271..161da42568 100644 --- a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectDeserializer.kt +++ b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectDeserializer.kt @@ -36,7 +36,7 @@ object ProjectDeserializer { fun deserializeProject(input: InputStream): Project? { val content = CompressedStreamHandler.wrapInput(input) - val projectString = content.mapLines { it }.joinToString(separator = "") { it } + val projectString = ProjectInputReader.extractProjectString(content) if (projectString.length <= 1) return null return try { diff --git a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReader.kt b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReader.kt new file mode 100644 index 0000000000..f45de47018 --- /dev/null +++ b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReader.kt @@ -0,0 +1,105 @@ +package de.maibornwolff.codecharta.serialization + +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParserSyncFlag +import java.io.BufferedInputStream +import java.io.BufferedReader +import java.io.InputStream +import java.io.InputStreamReader +import java.nio.charset.StandardCharsets +import java.util.Scanner + +object ProjectInputReader { + /** + * Extracts a JSON string representing a project from the given InputStream. + * Because piped bash commands run concurrently, a pipeable ccsh-parser sends a sync flag + * to signal other parsers to check for piped input. + * A short wait ensures the availability of potential sync flags. + * + * @param input InputStream with serialized project data. + * @return JSON string of the project, or an empty string if no valid data is found. + */ + fun extractProjectString(input: InputStream): String { + Thread.sleep(100) + val availableBytes = input.available() + + if (availableBytes <= 0) { + return "" + } + if (!input.markSupported()) { + return extractProjectString(BufferedInputStream(input)) + } + + val isSyncSignalContained = isSyncSignalContained(input, availableBytes) + + if (isSyncSignalContained) { + val scanner = Scanner(input) + val stringBuilder = StringBuilder() + + while (scanner.hasNextLine()) { + stringBuilder.append(scanner.nextLine()) + } + + return extractJsonObjectFromEndOfStream(stringBuilder.toString()) + } + + val charBuffer = CharArray(1024) + val reader = BufferedReader(InputStreamReader(input, StandardCharsets.UTF_8)) + val stringBuilder = StringBuilder() + while (reader.ready()) { + val bytesRead = reader.read(charBuffer) + stringBuilder.appendRange(charBuffer, 0, bytesRead) + } + val content = stringBuilder.toString() + return content.replace(Regex("[\\n\\r]"), "") + } + + private fun isSyncSignalContained(input: InputStream, availableBytes: Int): Boolean { + + val bufferSize = minOf(availableBytes, 1024) + val buffer = ByteArray(bufferSize) + input.mark(bufferSize) + input.read(buffer, 0, bufferSize) + + val syncFlag = PipeableParserSyncFlag.SYNC_FLAG.value + val syncSignalBytes = syncFlag.toByteArray() + input.reset() + return isSubarray(syncSignalBytes, buffer) + } + + private fun extractJsonObjectFromEndOfStream(streamContent: String): String { + var count = 0 + val openingBracket = '{' + val closingBracket = '}' + val lastClosingBracketIndex = streamContent.lastIndexOf(closingBracket) + if (lastClosingBracketIndex == -1) { + return streamContent + } + + var index = lastClosingBracketIndex + while (index >= 0) { + when (streamContent[index]) { + closingBracket -> count++ + openingBracket -> count-- + } + if (count == 0) { + break + } + index-- + } + + return if (count == 0) { + streamContent.substring(index, lastClosingBracketIndex + 1) + } else { + streamContent + } + } + + private fun isSubarray(subarray: ByteArray, buffer: ByteArray): Boolean { + for (i in 0 until buffer.size - subarray.size + 1) { + if (buffer.copyOfRange(i, i + subarray.size).contentEquals(subarray)) { + return true + } + } + return false + } +} diff --git a/analysis/model/src/test/kotlin/de/maibornwolff/codecharta/serialization/ProjectDeserializerTest.kt b/analysis/model/src/test/kotlin/de/maibornwolff/codecharta/serialization/ProjectDeserializerTest.kt index 1dd6cd094d..0b95ee9c8d 100644 --- a/analysis/model/src/test/kotlin/de/maibornwolff/codecharta/serialization/ProjectDeserializerTest.kt +++ b/analysis/model/src/test/kotlin/de/maibornwolff/codecharta/serialization/ProjectDeserializerTest.kt @@ -2,7 +2,10 @@ package de.maibornwolff.codecharta.serialization import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import java.io.PipedInputStream +import java.io.PipedOutputStream import java.io.StringReader +import java.nio.charset.StandardCharsets class ProjectDeserializerTest { val EXAMPLE_JSON_VERSION_1_0 = "example.cc.json" @@ -12,51 +15,66 @@ class ProjectDeserializerTest { @Test fun `should deserialize project from cc json with api version 1_2 or lower`() { + // given val expectedJsonReader = this.javaClass.classLoader.getResourceAsStream(EXAMPLE_JSON_VERSION_1_0)!!.reader() + // when val project = ProjectDeserializer.deserializeProject(expectedJsonReader) + // then assertThat(project.projectName).isEqualTo("201701poolobject") assertThat(project.size).isEqualTo(6) } @Test fun `should deserialize project from cc json with api version greater or equal than 1_3`() { + // given val expectedJsonReader = this.javaClass.classLoader.getResourceAsStream(EXAMPLE_JSON_VERSION_1_3)!!.reader() + // when val project = ProjectDeserializer.deserializeProject(expectedJsonReader) + // then assertThat(project.projectName).isEqualTo("201701poolobject") assertThat(project.size).isEqualTo(6) } @Test fun `should deserialize project from cc json string with api version 1_2 or lower`() { + // given val expectedJsonString = this.javaClass.classLoader.getResource(EXAMPLE_JSON_VERSION_1_0)!!.readText() + // when val project = ProjectDeserializer.deserializeProject(expectedJsonString) + // then assertThat(project.projectName).isEqualTo("201701poolobject") assertThat(project.size).isEqualTo(6) } @Test fun `should deserialize project from cc json string with api version greater or equal than 1_3`() { + // given val expectedJsonReader = this.javaClass.classLoader.getResource(EXAMPLE_JSON_VERSION_1_3)!!.readText() + // when val project = ProjectDeserializer.deserializeProject(expectedJsonReader) + // then assertThat(project.projectName).isEqualTo("201701poolobject") assertThat(project.size).isEqualTo(6) } @Test fun `should deserialize project from json string and set nonexisting values to defaults`() { + // given val jsonString = "{projectName='some Project', apiVersion='1.0', nodes=[{name:'root',type:'Folder'}]}" + // when val project = ProjectDeserializer.deserializeProject(StringReader(jsonString)) val node = project.rootNode + // then assertThat(node.link).isNull() assertThat(node.attributes).isNotNull assertThat(node.children).isNotNull @@ -64,8 +82,13 @@ class ProjectDeserializerTest { @Test fun `should deserialize project from cc json gz with api version 1_2 or lower`() { + // given val expectedInputStream = this.javaClass.classLoader.getResourceAsStream(EXAMPLE_JSON_GZ_VERSION_1_0)!! + + // when val project = ProjectDeserializer.deserializeProject(expectedInputStream) + + // then assertThat(project).isNotNull assertThat(project!!.projectName).isEqualTo("201701poolobject") assertThat(project.size).isEqualTo(6) @@ -73,11 +96,30 @@ class ProjectDeserializerTest { @Test fun `should deserialize project from cc json gz with api version greater or equal than 1_3`() { + // given val expectedInputStream = this.javaClass.classLoader.getResourceAsStream(EXAMPLE_JSON_GZ_VERSION_1_3)!! + // when val project = ProjectDeserializer.deserializeProject(expectedInputStream) + + // then assertThat(project).isNotNull assertThat(project!!.projectName).isEqualTo("201701poolobject") assertThat(project.size).isEqualTo(6) } + + @Test + fun `should not create deserialized project when input is not valid project`() { + // given + val invalidInput = "This is \n invalid \t data" + val inputStream = PipedInputStream() + val outputStream = PipedOutputStream(inputStream) + outputStream.write(invalidInput.toByteArray(StandardCharsets.UTF_8)) + + // when + val project = ProjectDeserializer.deserializeProject(inputStream) + + // then + assertThat(project).isNull() + } } diff --git a/analysis/model/src/test/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReaderTest.kt b/analysis/model/src/test/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReaderTest.kt new file mode 100644 index 0000000000..111e94df4f --- /dev/null +++ b/analysis/model/src/test/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReaderTest.kt @@ -0,0 +1,122 @@ +package de.maibornwolff.codecharta.serialization + +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParserSyncFlag +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Timeout +import java.io.PipedInputStream +import java.io.PipedOutputStream +import java.nio.charset.StandardCharsets +import java.util.concurrent.TimeUnit + +@Timeout(value = 15, unit = TimeUnit.SECONDS) +class ProjectInputReaderTest { + @Test + fun `Should not wait for input when pipeable parser sync flag is not set`() { + // given + val line1 = "line1" + val line2 = "line2" + val inputStream = PipedInputStream() + val outputStream = PipedOutputStream(inputStream) + outputStream.write(line1.toByteArray(StandardCharsets.UTF_8)) + + // when + Thread { + Thread.sleep(5000) + outputStream.write(line2.toByteArray(StandardCharsets.UTF_8)) + outputStream.close() + }.start() + + val linesRead = ProjectInputReader.extractProjectString(inputStream) + + // then + Assertions.assertThat(linesRead).isEqualTo(line1) + } + + @Test + fun `Should wait for input when pipeable parser sync flag is set`() { + // given + val syncFlag = PipeableParserSyncFlag.SYNC_FLAG.value + val line1 = "{\"data\":\"data\"}" + val inputStream = PipedInputStream() + val outputStream = PipedOutputStream(inputStream) + outputStream.write(syncFlag.toByteArray(StandardCharsets.UTF_8)) + + // when + Thread { + Thread.sleep(5000) + outputStream.write(line1.toByteArray(StandardCharsets.UTF_8)) + outputStream.close() + }.start() + + val linesRead = ProjectInputReader.extractProjectString(inputStream) + + // then + assertEquals(line1, linesRead) + } + + @Test + fun `Should discard newline characters when input contains newline characters`() { + // given + val syncFlag = PipeableParserSyncFlag.SYNC_FLAG.value + val line1 = "line1" + val line2 = "line2" + val newLine = "\n" + + val inputStream = PipedInputStream() + val outputStream = PipedOutputStream(inputStream) + outputStream.write(syncFlag.toByteArray(StandardCharsets.UTF_8)) + outputStream.write(line1.toByteArray(StandardCharsets.UTF_8)) + outputStream.write(newLine.toByteArray(StandardCharsets.UTF_8)) + outputStream.write(line2.toByteArray(StandardCharsets.UTF_8)) + outputStream.write(newLine.toByteArray(StandardCharsets.UTF_8)) + outputStream.close() + + // when + val linesRead = ProjectInputReader.extractProjectString(inputStream) + + // then + Assertions.assertThat(linesRead).isEqualTo(line1 + line2) + } + + @Test + fun `Should discard input before project string when project comes at end of stream`() { + // given + val syncFlag = PipeableParserSyncFlag.SYNC_FLAG.value + val line1 = "line1" + val line2 = "{\"data\":\"data\"}" + + val inputStream = PipedInputStream() + val outputStream = PipedOutputStream(inputStream) + outputStream.write(syncFlag.toByteArray(StandardCharsets.UTF_8)) + outputStream.write(line1.toByteArray(StandardCharsets.UTF_8)) + outputStream.write(line2.toByteArray(StandardCharsets.UTF_8)) + outputStream.close() + + // when + val linesRead = ProjectInputReader.extractProjectString(inputStream) + + // then + Assertions.assertThat(linesRead).isEqualTo(line2) + } + + @Test + fun `Should return stream content when no valid project at end of stream`() { + // given + val syncFlag = PipeableParserSyncFlag.SYNC_FLAG.value + val invalidProjectData = "data\":\"data\"}" + + val inputStream = PipedInputStream() + val outputStream = PipedOutputStream(inputStream) + outputStream.write(syncFlag.toByteArray(StandardCharsets.UTF_8)) + outputStream.write(invalidProjectData.toByteArray(StandardCharsets.UTF_8)) + outputStream.close() + + // when + val linesRead = ProjectInputReader.extractProjectString(inputStream) + + // then + Assertions.assertThat(linesRead).isEqualTo(invalidProjectData) + } +} diff --git a/analysis/parser/RawTextParser/build.gradle b/analysis/parser/RawTextParser/build.gradle index 1bef8c4eb0..c7be25c907 100644 --- a/analysis/parser/RawTextParser/build.gradle +++ b/analysis/parser/RawTextParser/build.gradle @@ -2,6 +2,7 @@ dependencies { implementation project(':model') implementation project(':filter:MergeFilter') implementation project(':tools:InteractiveParser') + implementation project(':tools:PipeableParser') implementation group: 'info.picocli', name: 'picocli', version: picocli_version implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlin_logging_version diff --git a/analysis/parser/RawTextParser/src/main/kotlin/de/maibornwolff/codecharta/parser/rawtextparser/RawTextParser.kt b/analysis/parser/RawTextParser/src/main/kotlin/de/maibornwolff/codecharta/parser/rawtextparser/RawTextParser.kt index e306027726..d063f730a8 100644 --- a/analysis/parser/RawTextParser/src/main/kotlin/de/maibornwolff/codecharta/parser/rawtextparser/RawTextParser.kt +++ b/analysis/parser/RawTextParser/src/main/kotlin/de/maibornwolff/codecharta/parser/rawtextparser/RawTextParser.kt @@ -5,6 +5,8 @@ import de.maibornwolff.codecharta.serialization.ProjectSerializer import de.maibornwolff.codecharta.tools.interactiveparser.InteractiveParser import de.maibornwolff.codecharta.tools.interactiveparser.ParserDialogInterface import de.maibornwolff.codecharta.tools.interactiveparser.util.CodeChartaConstants +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParser +import de.maibornwolff.codecharta.tools.pipeableparser.PipeableParserSyncFlag import de.maibornwolff.codecharta.util.CommaSeparatedStringToListConverter import de.maibornwolff.codecharta.util.FileExtensionConverter import de.maibornwolff.codecharta.util.InputHelper @@ -25,7 +27,7 @@ class RawTextParser( private val input: InputStream = System.`in`, private val output: PrintStream = System.out, private val error: PrintStream = System.err, -) : Callable, InteractiveParser { +) : Callable, InteractiveParser, PipeableParser { private val logger = KotlinLogging.logger {} @@ -92,8 +94,9 @@ class RawTextParser( } @Throws(IOException::class) - override fun call(): Void? { - print(" ") + override fun call(): Unit? { + logPipeableParserSyncSignal(PipeableParserSyncFlag.SYNC_FLAG) + if (!InputHelper.isInputValidAndNotNull(arrayOf(inputFile), canInputContainFolders = true)) { throw IllegalArgumentException("Input invalid file for RawTextParser, stopping execution...") } diff --git a/analysis/settings.gradle b/analysis/settings.gradle index fd6be04e59..162e935ab7 100644 --- a/analysis/settings.gradle +++ b/analysis/settings.gradle @@ -3,6 +3,9 @@ include 'filter:MergeFilter', 'filter:EdgeFilter', 'filter:StructureModifier' include 'import:CSVImporter', 'import:SVNLogParser', 'import:GitLogParser', 'import:SonarImporter', 'import:SourceCodeParser', 'import:CodeMaatImporter', 'import:TokeiImporter', 'import:MetricGardenerImporter' include 'parser:RawTextParser' include 'export:CSVExporter' -include 'tools:ValidationTool', 'tools:ccsh', 'tools:InteractiveParser' +include 'tools:ValidationTool', 'tools:ccsh', 'tools:InteractiveParser', 'tools:PipeableParser' rootProject.name = 'codecharta' +include 'tools:PipeableParser' +findProject(':tools:PipeableParser')?.name = 'PipeableParser' + diff --git a/analysis/tools/PipeableParser/build.gradle b/analysis/tools/PipeableParser/build.gradle new file mode 100644 index 0000000000..9595688b39 --- /dev/null +++ b/analysis/tools/PipeableParser/build.gradle @@ -0,0 +1,7 @@ +repositories { + mavenCentral() +} + +dependencies { + +} diff --git a/analysis/tools/PipeableParser/src/main/kotlin/de/maibornwolff/codecharta/tools/pipeableparser/PipeableParser.kt b/analysis/tools/PipeableParser/src/main/kotlin/de/maibornwolff/codecharta/tools/pipeableparser/PipeableParser.kt new file mode 100644 index 0000000000..13ea07e6ae --- /dev/null +++ b/analysis/tools/PipeableParser/src/main/kotlin/de/maibornwolff/codecharta/tools/pipeableparser/PipeableParser.kt @@ -0,0 +1,7 @@ +package de.maibornwolff.codecharta.tools.pipeableparser + +interface PipeableParser { + fun logPipeableParserSyncSignal(syncSignal: PipeableParserSyncFlag) { + print(syncSignal.value) + } +} diff --git a/analysis/tools/PipeableParser/src/main/kotlin/de/maibornwolff/codecharta/tools/pipeableparser/PipeableParserSyncFlag.kt b/analysis/tools/PipeableParser/src/main/kotlin/de/maibornwolff/codecharta/tools/pipeableparser/PipeableParserSyncFlag.kt new file mode 100644 index 0000000000..4b2a678880 --- /dev/null +++ b/analysis/tools/PipeableParser/src/main/kotlin/de/maibornwolff/codecharta/tools/pipeableparser/PipeableParserSyncFlag.kt @@ -0,0 +1,5 @@ +package de.maibornwolff.codecharta.tools.pipeableparser + +enum class PipeableParserSyncFlag(val value: String) { + SYNC_FLAG("\r\r\r\r\r") +} diff --git a/analysis/tools/ValidationTool/src/main/kotlin/de/maibornwolff/codecharta/tools/validation/ValidationTool.kt b/analysis/tools/ValidationTool/src/main/kotlin/de/maibornwolff/codecharta/tools/validation/ValidationTool.kt index 138c851143..76b7c28441 100644 --- a/analysis/tools/ValidationTool/src/main/kotlin/de/maibornwolff/codecharta/tools/validation/ValidationTool.kt +++ b/analysis/tools/ValidationTool/src/main/kotlin/de/maibornwolff/codecharta/tools/validation/ValidationTool.kt @@ -14,7 +14,7 @@ import java.util.concurrent.Callable description = [ValidationTool.DESCRIPTION], footer = [CodeChartaConstants.General.GENERIC_FOOTER] ) -class ValidationTool : Callable, InteractiveParser { +class ValidationTool : Callable, InteractiveParser { @CommandLine.Option(names = ["-h", "--help"], usageHelp = true, description = ["displays this help and exits"]) var help: Boolean = false @@ -32,7 +32,7 @@ class ValidationTool : Callable, InteractiveParser { const val SCHEMA_PATH = "cc.json" } - override fun call(): Void? { + override fun call(): Unit? { if (!InputHelper.isInputValidAndNotNull(arrayOf(file), canInputContainFolders = false)) { throw IllegalArgumentException("Input invalid file for ValidationTool, stopping execution...") } diff --git a/analysis/tools/ccsh/build.gradle b/analysis/tools/ccsh/build.gradle index 6355a8f1b3..37cb4a68b3 100644 --- a/analysis/tools/ccsh/build.gradle +++ b/analysis/tools/ccsh/build.gradle @@ -33,6 +33,7 @@ dependencies { def csvExporter = project(':export:CSVExporter') def rawTextParser = project(':parser:RawTextParser') def interactiveParser = project(':tools:InteractiveParser') + def pipeableParser = project(':tools:PipeableParser') def metricGardenerImporter = project(':import:MetricGardenerImporter') // first implementation is for dependency in main, testImplementation is so our test suite can find all other tests @@ -50,6 +51,7 @@ dependencies { implementation structureModifier; testImplementation structureModifier.sourceSets.test.output implementation rawTextParser; testImplementation rawTextParser.sourceSets.test.output implementation interactiveParser; testImplementation interactiveParser.sourceSets.test.output + implementation pipeableParser; testImplementation pipeableParser.sourceSets.test.output implementation metricGardenerImporter; testImplementation metricGardenerImporter.sourceSets.test.output implementation group: 'info.picocli', name: 'picocli', version: picocli_version @@ -68,8 +70,8 @@ jar { duplicatesStrategy = DuplicatesStrategy.EXCLUDE manifest { attributes 'Main-Class': mainClassName, - 'Implementation-Title': 'CodeCharta ccsh', - 'Implementation-Version': archiveVersion + 'Implementation-Title': 'CodeCharta ccsh', + 'Implementation-Version': archiveVersion } zip64 true exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA' @@ -87,5 +89,5 @@ startScripts{ } test { - useJUnitPlatform() + useJUnitPlatform() } diff --git a/analysis/tools/ccsh/src/main/kotlin/de/maibornwolff/codecharta/tools/ccsh/Ccsh.kt b/analysis/tools/ccsh/src/main/kotlin/de/maibornwolff/codecharta/tools/ccsh/Ccsh.kt index 798c8533d1..12a09fbb61 100644 --- a/analysis/tools/ccsh/src/main/kotlin/de/maibornwolff/codecharta/tools/ccsh/Ccsh.kt +++ b/analysis/tools/ccsh/src/main/kotlin/de/maibornwolff/codecharta/tools/ccsh/Ccsh.kt @@ -56,7 +56,7 @@ import kotlin.system.exitProcess footer = [CodeChartaConstants.General.GENERIC_FOOTER] ) -class Ccsh : Callable { +class Ccsh : Callable { @CommandLine.Option( names = ["-v", "--version"], @@ -71,7 +71,7 @@ class Ccsh : Callable { @CommandLine.Option(names = ["-i", "--interactive"], description = ["starts interactive parser"]) var shouldUseInteractiveShell: Boolean = false - override fun call(): Void? { + override fun call(): Unit? { // info: always run return null @@ -245,9 +245,9 @@ class Ccsh : Callable { } @CommandLine.Command(name = "install", description = ["[deprecated]: does nothing"]) -class Installer : Callable { +class Installer : Callable { - override fun call(): Void? { + override fun call(): Unit? { println("[deprecated]: does nothing") return null }