commit 644c712cc7fa3c54e6e0411c853b1f61bb872bf7 Author: Erik C. Thauvin Date: Mon Apr 17 17:54:43 2017 -0700 First commit. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6ec2ae2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# batch files are specific to windows and always crlf +*.bat eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8775852 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +**/.idea/dictionaries +**/.idea/gradle.xml +**/.idea/libraries +**/.idea/tasks.xml +**/.idea/workspace.xml +*.iws +.classpath +.DS_Store +.gradle +.kobalt +.nb-gradle +.project +.settings +/bin +/build +/deploy +/dist +/gen +/gradle.properties +/libs +/local.properties +/out +/proguard-project.txt +/project.properties +/target +/test-output +ehthumbs.db +kobaltBuild +Thumbs.db \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..8ff795e --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,53 @@ + + + + \ No newline at end of file diff --git a/.idea/kobalt.xml b/.idea/kobalt.xml new file mode 100644 index 0000000..834a200 --- /dev/null +++ b/.idea/kobalt.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..1c24f9a --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6ebe78f --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + Java + + + Portability issuesJava + + + + + Android + + + + + + + + + + + + + commons-logging-api + + + + + + + + 1.8.x + + + + + + + + 1.8.x + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..ad2960d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 0000000..861c1ef --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,27 @@ +Copyright (c) 2017, Erik C. Thauvin (erik@thauvin.net) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of this project nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/example/kobalt/src/Build.kt b/example/kobalt/src/Build.kt new file mode 100644 index 0000000..5a3decb --- /dev/null +++ b/example/kobalt/src/Build.kt @@ -0,0 +1,37 @@ +import com.beust.kobalt.* +import com.beust.kobalt.plugin.packaging.* +import com.beust.kobalt.plugin.application.* +import com.beust.kobalt.plugin.kotlin.* +import net.thauvin.erik.kobalt.plugin.propertyfile.* + +val bs = buildScript { + plugins(file("../libs/kobalt-property-file-0.1.0.jar")) +} + +val p = project { + name = "example" + group = "com.example" + artifactId = name + version = "0.1" + + dependencies { +// compile("com.beust:jcommander:1.68") + } + + dependenciesTest { + compile("org.testng:testng:6.11") + } + + assemble { + jar { + } + } + + application { + mainClass = "com.example.MainKt" + } + + propertyFile { + file = "version.properties" + } +} diff --git a/example/kobalt/wrapper/kobalt-wrapper.jar b/example/kobalt/wrapper/kobalt-wrapper.jar new file mode 100644 index 0000000..08165f6 Binary files /dev/null and b/example/kobalt/wrapper/kobalt-wrapper.jar differ diff --git a/example/kobalt/wrapper/kobalt-wrapper.properties b/example/kobalt/wrapper/kobalt-wrapper.properties new file mode 100644 index 0000000..aa4491a --- /dev/null +++ b/example/kobalt/wrapper/kobalt-wrapper.properties @@ -0,0 +1 @@ +kobalt.version=1.0.67 diff --git a/example/kobaltw b/example/kobaltw new file mode 100644 index 0000000..c5186d5 --- /dev/null +++ b/example/kobaltw @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +java -jar "`dirname "$0"`/kobalt/wrapper/kobalt-wrapper.jar" $* diff --git a/example/kobaltw.bat b/example/kobaltw.bat new file mode 100644 index 0000000..d578071 --- /dev/null +++ b/example/kobaltw.bat @@ -0,0 +1,4 @@ +@echo off +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +java -jar "%DIRNAME%/kobalt/wrapper/kobalt-wrapper.jar" %* diff --git a/example/src/main/kotlin/com/example/Main.kt b/example/src/main/kotlin/com/example/Main.kt new file mode 100644 index 0000000..1df958f --- /dev/null +++ b/example/src/main/kotlin/com/example/Main.kt @@ -0,0 +1,3 @@ +package com.example + +fun main(args: Array) = println("\n\nHello Kotlin world from Kobalt\n\n") \ No newline at end of file diff --git a/example/src/test/kotlin/com/example/MainTest.kt b/example/src/test/kotlin/com/example/MainTest.kt new file mode 100644 index 0000000..96458e9 --- /dev/null +++ b/example/src/test/kotlin/com/example/MainTest.kt @@ -0,0 +1,8 @@ +package com.example + +import org.testng.annotations.Test + +class ExampleTest { + @Test + fun f() = println("Running test") +} \ No newline at end of file diff --git a/kobalt-property-file.iml b/kobalt-property-file.iml new file mode 100644 index 0000000..8b46963 --- /dev/null +++ b/kobalt-property-file.iml @@ -0,0 +1,628 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kobalt/Build.kt.iml b/kobalt/Build.kt.iml new file mode 100644 index 0000000..2d42ea5 --- /dev/null +++ b/kobalt/Build.kt.iml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt new file mode 100644 index 0000000..cbaab6e --- /dev/null +++ b/kobalt/src/Build.kt @@ -0,0 +1,57 @@ + +import com.beust.kobalt.plugin.application.application +import com.beust.kobalt.plugin.packaging.assemble +import com.beust.kobalt.profile +import com.beust.kobalt.project +//import net.thauvin.erik.kobalt.plugin.propertyfile.* + +val semver = "0.1.0" + +val dev by profile() +val kobaltDependency = if (dev) "kobalt" else "kobalt-plugin-api" + +val p = project { + name = "kobalt-property-file" + group = "net.thauvin.erik" + artifactId = name + version = semver + + dependencies { + compile("com.beust:$kobaltDependency:") + } + + dependenciesTest { + //compile("org.testng:testng:6.11") + } + + assemble { + jar { + fatJar = true + } + + mavenJars {} + } + + application { + mainClass = "com.example.MainKt" + } +} + +val example = project(p) { + directory = "example" + name = "example" + version = "0.1.0" + + assemble { + jar { + } + } + + application { + mainClass = "com.example.MainKt" + } + +// propertyFile { +// file = "version.properties" +// } +} \ No newline at end of file diff --git a/kobalt/wrapper/kobalt-wrapper.jar b/kobalt/wrapper/kobalt-wrapper.jar new file mode 100644 index 0000000..64f1427 Binary files /dev/null and b/kobalt/wrapper/kobalt-wrapper.jar differ diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties new file mode 100644 index 0000000..2f7d9ed --- /dev/null +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -0,0 +1 @@ +kobalt.version=1.0.68 \ No newline at end of file diff --git a/kobaltw b/kobaltw new file mode 100644 index 0000000..c5186d5 --- /dev/null +++ b/kobaltw @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +java -jar "`dirname "$0"`/kobalt/wrapper/kobalt-wrapper.jar" $* diff --git a/kobaltw.bat b/kobaltw.bat new file mode 100644 index 0000000..d578071 --- /dev/null +++ b/kobaltw.bat @@ -0,0 +1,4 @@ +@echo off +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +java -jar "%DIRNAME%/kobalt/wrapper/kobalt-wrapper.jar" %* diff --git a/src/main/kotlin/net/thauvin/erik/kobalt/plugin/propertyfile/PropertyFilePlugin.kt b/src/main/kotlin/net/thauvin/erik/kobalt/plugin/propertyfile/PropertyFilePlugin.kt new file mode 100644 index 0000000..47de875 --- /dev/null +++ b/src/main/kotlin/net/thauvin/erik/kobalt/plugin/propertyfile/PropertyFilePlugin.kt @@ -0,0 +1,257 @@ +package net.thauvin.erik.kobalt.plugin.propertyfile + +import com.beust.kobalt.Plugins +import com.beust.kobalt.TaskResult +import com.beust.kobalt.api.* +import com.beust.kobalt.api.annotation.Directive +import com.beust.kobalt.api.annotation.Task +import com.beust.kobalt.misc.error +import com.beust.kobalt.misc.warn +import com.google.inject.Inject +import com.google.inject.Singleton +import java.io.FileOutputStream +import java.nio.file.Files +import java.nio.file.Paths +import java.text.* +import java.util.* + +@Singleton +class PropertyFilePlugin @Inject constructor(val configActor: ConfigActor, + val taskContributor: TaskContributor) : + BasePlugin(), ITaskContributor, IConfigActor by configActor { + private val calendarFields = mapOf( + Units.MILLISECOND to Calendar.MILLISECOND, + Units.SECOND to Calendar.SECOND, + Units.MINUTE to Calendar.MINUTE, + Units.HOUR to Calendar.HOUR_OF_DAY, + Units.DAY to Calendar.DATE, + Units.WEEK to Calendar.WEEK_OF_YEAR, + Units.MONTH to Calendar.MONTH, + Units.YEAR to Calendar.YEAR + ) + + // ITaskContributor + override fun tasksFor(project: Project, context: KobaltContext): List { + return emptyList() + } + + companion object { + const val NAME: String = "PropertyFile" + } + + override val name = NAME + + override fun apply(project: Project, context: KobaltContext) { + super.apply(project, context) + taskContributor.addVariantTasks(this, project, context, NAME, group = "other", + runTask = { propertyFile(project) }) + } + + @Task(name = "propertyFile", description = "Edit a property file.") + fun propertyFile(project: Project): TaskResult { + configurationFor(project)?.let { config -> + if (config.file.isBlank()) { + error("Please specify a property file name.") + return TaskResult() + } else { + // Load properties + val p = Properties() + Paths.get(config.file).let { path -> + if (path.toFile().exists()) { + Files.newInputStream(path).use { + p.load(it) + } + } + } + + var result = TaskResult() + + // Process entries + config.entries.forEach { entry -> + if (entry.key.isBlank()) { + error("An entry key must be specified.") + return TaskResult() + } else { + with(entry) { + if (value == null && default == null && operation != Operations.DELETE) { + warn("An entry value or default must be specified: $key") + } else if (type == Types.STRING && (operation == Operations.SUBTRACT)) { + warn("Subtraction is not supported for String properties: $key") + } else if (operation == Operations.DELETE) { + p.remove(entry.key) + } else { + when (type) { + Types.DATE -> result = processDate(p, entry) + Types.INT -> result = processInt(p, entry) + else -> result = processString(p, entry) + } + } + } + } + + // @TODO maybe just warn and keep on going? + if (!result.success) { + return result + } + } + + // Save properties + FileOutputStream(config.file).use { output -> + p.store(output, config.comment) + } + } + } + + return TaskResult() + } + + private fun processDate(p: Properties, entry: Entry): TaskResult { + val cal = Calendar.getInstance() + val value = currentValue(p.getProperty(entry.key), entry.value, entry.default, entry.operation) + + val fmt = SimpleDateFormat(if (entry.pattern.isBlank()) { + "yyyy/MM/dd HH:mm" + } else { + entry.pattern + }) + + if (value.equals("now", true) || value.isBlank()) { + cal.time = Date() + } else { + try { + cal.time = fmt.parse(value) + } catch (pe: ParseException) { + warn("Date parse exception for: ${entry.key}", pe) + } + } + + if (entry.operation != Operations.SET) { + var offset = 0 + + try { + offset = Integer.parseInt(value) + if (entry.operation == Operations.SUBTRACT) { + offset *= -1 + } + } catch (nfe: NumberFormatException) { + warn("Non-integer value for: ${entry.key}") + } + + cal.add(calendarFields.getOrDefault(entry.unit, Calendar.DATE), offset) + } + + return TaskResult() + } + + private fun processInt(p: Properties, entry: Entry): TaskResult { + var intValue: Int + try { + val fmt = DecimalFormat(entry.pattern) + val value = currentValue(p.getProperty(entry.key), entry.value, entry.default, entry.operation) + + intValue = fmt.parse(if (value.isBlank()) "0" else value).toInt() + + if (entry.operation == Operations.ADD) { + intValue += 1 + } else if (entry.operation == Operations.SUBTRACT) { + intValue -= 1 + } + + p.setProperty(entry.key, fmt.format(intValue)) + } catch (nfe: NumberFormatException) { + warn("Number format exception for: ${entry.key}", nfe) + } catch (pe: ParseException) { + warn("Number parsing exception for: ${entry.key}", pe) + } + + return TaskResult() + } + + private fun processString(p: Properties, entry: Entry): TaskResult { + val value = currentValue(p.getProperty(entry.key), entry.value, entry.default, entry.operation) + + if (entry.operation == Operations.SET) { + p.setProperty(entry.key, value) + } else if (entry.operation == Operations.ADD) { + p.setProperty(entry.key, value + p.getProperty(entry.key, "")) + } + + return TaskResult() + } + + private fun currentValue(value: String?, newValue: String?, default: String?, operation: Enum): String { + var result: String? = null + + if (operation == Operations.SET) { + if (newValue != null && default != null) { + result = newValue + } + + if (newValue != null && default != null && value != null) { + result = value + } + + if (newValue != null && default != null && value == null) { + result = default + } + } else { + result = value ?: default + } + + if (result == null) { + result = "" + } + + return result + } +} + +enum class Types { + DATE, INT, STRING +} + +enum class Operations { + ADD, DELETE, SET, SUBTRACT +} + + +enum class Units { + MILLISECOND, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR +} + +data class Entry( + var key: String = "", + var value: String? = null, + var default: String? = null, + var type: Enum = Types.STRING, + var operation: Enum = Operations.SET, + var pattern: String = "", + var unit: Units = Units.DAY) + +@Directive +class PropertyFileConfig { + var file: String = "" + var comment: String = "" + val entries = arrayListOf() + + @Suppress("unused") + fun entry( + key: String = "", + value: String? = null, + default: String? = null, + type: Enum = Types.STRING, + operation: Enum = Operations.SET, + pattern: String = "", + unit: Units = Units.DAY) { + if (key.isNotEmpty()) entries.add(Entry(key, value, default, type, operation, pattern, unit)) + } +} + +@Suppress("unused") +@Directive +fun Project.propertyfile(init: PropertyFileConfig.() -> Unit) { + PropertyFileConfig().let { config -> + config.init() + (Plugins.findPlugin(PropertyFilePlugin.NAME) as PropertyFilePlugin).addConfiguration(this, config) + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/kobalt-plugin.xml b/src/main/resources/META-INF/kobalt-plugin.xml new file mode 100644 index 0000000..1463d1c --- /dev/null +++ b/src/main/resources/META-INF/kobalt-plugin.xml @@ -0,0 +1,6 @@ + + Maven Local Repository + + net.thauvin.erik.kobalt.plugin.propertyfile.PropertyFilePlugin + + \ No newline at end of file diff --git a/src/test/kotlin/com/example/MainTest.kt b/src/test/kotlin/com/example/MainTest.kt new file mode 100644 index 0000000..96458e9 --- /dev/null +++ b/src/test/kotlin/com/example/MainTest.kt @@ -0,0 +1,8 @@ +package com.example + +import org.testng.annotations.Test + +class ExampleTest { + @Test + fun f() = println("Running test") +} \ No newline at end of file