User: strokkur24 Date: 19 Jan 26 17:24 Revision: 8076ef0889f55ee89d9869e5e170f1b6d0ef2c3a Summary: Add forcedValue property TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10399&personal=false Index: src/main/kotlin/creator/custom/CreatorTemplateProcessor.kt =================================================================== --- src/main/kotlin/creator/custom/CreatorTemplateProcessor.kt (revision 0ff5c52310324fc559b905ba52aa1496a557ca77) +++ src/main/kotlin/creator/custom/CreatorTemplateProcessor.kt (revision 8076ef0889f55ee89d9869e5e170f1b6d0ef2c3a) @@ -205,7 +205,7 @@ reporter.fatal("Duplicate property name ${descriptor.name}") } - val prop = CreatorPropertyFactory.createFromType(descriptor.type, descriptor, context) + val prop = CreatorPropertyFactory.createFromType(descriptor.type, descriptor, context, reporter) ?: reporter.fatal("Unknown template property type ${descriptor.type}") prop.setupProperty(reporter) Index: src/main/kotlin/creator/custom/TemplateDescriptor.kt =================================================================== --- src/main/kotlin/creator/custom/TemplateDescriptor.kt (revision 0ff5c52310324fc559b905ba52aa1496a557ca77) +++ src/main/kotlin/creator/custom/TemplateDescriptor.kt (revision 8076ef0889f55ee89d9869e5e170f1b6d0ef2c3a) @@ -56,6 +56,7 @@ val groupProperties: List? = null, val remember: Any? = null, val visible: Any? = null, + val forceValue: Any? = null, val editable: Boolean? = null, val collapsible: Boolean? = null, val warning: String? = null, Index: src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt =================================================================== --- src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt (revision 0ff5c52310324fc559b905ba52aa1496a557ca77) +++ src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt (revision 8076ef0889f55ee89d9869e5e170f1b6d0ef2c3a) @@ -21,7 +21,6 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.asset.MCDevBundle -import com.demonwav.mcdev.asset.MCDevBundle.invoke import com.demonwav.mcdev.creator.collectMavenVersions import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.CreatorContext Index: src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt =================================================================== --- src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt (revision 0ff5c52310324fc559b905ba52aa1496a557ca77) +++ src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt (revision 8076ef0889f55ee89d9869e5e170f1b6d0ef2c3a) @@ -22,6 +22,7 @@ import com.demonwav.mcdev.creator.custom.CreatorContext import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.intellij.icons.AllIcons import com.intellij.ui.content.AlertIcon import com.intellij.ui.dsl.builder.Panel @@ -39,6 +40,9 @@ override fun deserialize(string: String): Boolean = string.toBoolean() + override fun handleForceValueProperty(original: Boolean, string: String): Boolean = + string.toBooleanStrictOrNull() ?: original + override fun buildSimpleUi(panel: Panel) { val label = descriptor.translatedLabel panel.row(label) { @@ -49,16 +53,31 @@ .comment(descriptor.translate(warning)) } - this.checkBox(label.removeSuffix(":").trim()) + val checkbox = this.checkBox(label.removeSuffix(":").trim()) .bindSelected(graphProperty) .enabled(descriptor.editable != false) + + var previousEnabledStatus = graphProperty.get() + forceValueProperty?.afterChange { str -> + if (str == null) { + checkbox.enabled(descriptor.editable != false) + graphProperty.set(previousEnabledStatus) + } else { + str.toBooleanStrictOrNull()?.let { + checkbox.enabled(false) + previousEnabledStatus = graphProperty.get() + graphProperty.set(it) + } + } + } }.propertyVisibility() } class Factory : CreatorPropertyFactory { override fun create( descriptor: TemplatePropertyDescriptor, - context: CreatorContext - ): CreatorProperty<*> = BooleanCreatorProperty(descriptor, context) + context: CreatorContext, + reporter: TemplateValidationReporter + ): CreatorProperty<*> = BooleanCreatorProperty(descriptor, context).initForceValueProperty(reporter) } } Index: src/main/kotlin/creator/custom/types/CreatorProperty.kt =================================================================== --- src/main/kotlin/creator/custom/types/CreatorProperty.kt (revision 0ff5c52310324fc559b905ba52aa1496a557ca77) +++ src/main/kotlin/creator/custom/types/CreatorProperty.kt (revision 8076ef0889f55ee89d9869e5e170f1b6d0ef2c3a) @@ -55,6 +55,8 @@ private var derivation: PreparedDerivation? = null private lateinit var visibleProperty: GraphProperty + var forceValueProperty: GraphProperty? = null + abstract val graphProperty: GraphProperty abstract fun createDefaultValue(raw: Any?): T @@ -63,11 +65,18 @@ abstract fun deserialize(string: String): T + open fun handleForceValueProperty(original: T, string: String): T = original + open fun toStringProperty(graphProperty: GraphProperty): ObservableMutableProperty = graphProperty.transform(::serialize, ::deserialize) open fun get(): T? { - val value = graphProperty.get() + val forcedValue = forceValueProperty?.get() + var value = graphProperty.get() + if (forcedValue != null) { + value = handleForceValueProperty(value, forcedValue) + } + if (descriptor.nullIfDefault == true) { val default = createDefaultValue(descriptor.default) if (value == default) { @@ -234,30 +243,24 @@ return setupVisibleProperty(graph, properties, reporter, visibility) } + fun initForceValueProperty(reporter: TemplateValidationReporter): CreatorProperty { + this.forceValueProperty = setupForceValueProperty(graph, properties, reporter, descriptor.forceValue) + return this + } + companion object { - fun setupVisibleProperty( - graph: PropertyGraph, + private fun obtainDependencies( properties: Map>, reporter: TemplateValidationReporter, - visibility: Any? - ): GraphProperty { - val prop = graph.property(true) - if (visibility == null || visibility is Boolean) { - prop.set(visibility != false) - return prop - } - - if (visibility !is Map<*, *>) { - reporter.error("Visibility can only be a boolean or an object") - return prop - } - - val dependsOn = visibility["dependsOn"] + property: Map<*, *>, + name: String + ): List>? { + val dependsOn = property["dependsOn"] if (dependsOn !is String && (dependsOn !is List<*> || dependsOn.any { it !is String })) { reporter.error( - "Expected 'visible' to have a 'dependsOn' value that is either a string or a list of strings" + "Expected '$name' to have a 'dependsOn' value that is either a string or a list of strings" ) - return prop + return null } val dependenciesNames = when (dependsOn) { @@ -268,18 +271,28 @@ val dependencies = dependenciesNames.mapNotNull { val dependency = properties[it] if (dependency == null) { - reporter.error("Visibility dependency '$it' does not exist") + reporter.error("'$name' dependency '$it' does not exist") } dependency } if (dependencies.size != dependenciesNames.size) { // Errors have already been reported - return prop + return null } - val condition = visibility["condition"] + return dependencies + } + + private fun setupDependableProperty( + dependencies: List>, + reporter: TemplateValidationReporter, + property: Map<*, *>, + name: String, + prop: GraphProperty + ): GraphProperty { + val condition = property["condition"] if (condition !is String) { - reporter.error("Expected 'visible' to have a 'condition' string") + reporter.error("Expected '$name' to have a 'condition' string") return prop } @@ -291,10 +304,10 @@ if (exception != null) { if (!didInitialUpdate) { didInitialUpdate = true - reporter.error("Failed to compute initial visibility: ${exception.message}") - thisLogger().info("Failed to compute initial visibility: ${exception.message}", exception) + reporter.error("Failed to compute initial $name-property: ${exception.message}") + thisLogger().info("Failed to compute initial $name-property: ${exception.message}", exception) } else { - thisLogger().error("Failed to compute initial visibility: ${exception.message}", exception) + thisLogger().error("Failed to compute initial $name-property: ${exception.message}", exception) } } @@ -308,5 +321,80 @@ return prop } + + fun setupVisibleProperty( + graph: PropertyGraph, + properties: Map>, + reporter: TemplateValidationReporter, + visibility: Any? + ): GraphProperty { + val prop = graph.property(true) + if (visibility == null || visibility is Boolean) { + prop.set(visibility != false) + return prop - } + } + + if (visibility !is Map<*, *>) { + reporter.error("Visibility can only be a boolean or an object") + return prop -} + } + + val dependencies = obtainDependencies(properties, reporter, visibility, "visible") + ?: return prop // The error has already been reported + + return setupDependableProperty(dependencies, reporter, visibility, "visible", prop) + } + + fun setupForceValueProperty( + graph: PropertyGraph, + properties: Map>, + reporter: TemplateValidationReporter, + forceValue: Any? + ): GraphProperty { + val out = graph.property(null) + + if (forceValue == null) { + return out; + } + + if (forceValue !is Map<*, *>) { + reporter.error("ForceValue can only be a boolean or an object") + return out + } + + val dependencies = obtainDependencies(properties, reporter, forceValue, "forceValue") + ?: return out // The error has already been reported + + val value = forceValue["value"] + if (value !is String) { + reporter.error("Expected 'forceValue' to have a 'value' string") + return out + } + + val conditionProperty = + setupDependableProperty(dependencies, reporter, forceValue, "forceValue", graph.property(false)) + + val update: () -> String? = { + if (conditionProperty.get()) { + val conditionProperties = dependencies.associate { prop -> prop.descriptor.name to prop.get() } + val result = TemplateEvaluator.template(conditionProperties, value) + val exception = result.exceptionOrNull() + if (exception != null) { + thisLogger().error("Failed to compute forceValue-property: ${exception.message}", exception) + } + + result.getOrNull() + } else { + null + } + } + + out.set(update()) + for (dependency in dependencies) { + out.dependsOn(dependency.graphProperty, deleteWhenModified = false, update) + } + + return out + } + } +} Index: src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt =================================================================== --- src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt (revision 0ff5c52310324fc559b905ba52aa1496a557ca77) +++ src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt (revision 8076ef0889f55ee89d9869e5e170f1b6d0ef2c3a) @@ -22,6 +22,7 @@ import com.demonwav.mcdev.creator.custom.CreatorContext import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.RequiredElement import com.intellij.openapi.util.KeyedExtensionCollector @@ -42,15 +43,22 @@ fun createFromType( type: String, descriptor: TemplatePropertyDescriptor, - context: CreatorContext + context: CreatorContext, + reporter: TemplateValidationReporter ): CreatorProperty<*>? { - return COLLECTOR.findSingle(type)?.create(descriptor, context) + return COLLECTOR.findSingle(type)?.create(descriptor, context, reporter) } } - fun create(descriptor: TemplatePropertyDescriptor, context: CreatorContext): CreatorProperty<*> + fun create(descriptor: TemplatePropertyDescriptor, context: CreatorContext, reporter: TemplateValidationReporter): CreatorProperty<*> { + return create(descriptor, context) -} + } + fun create(descriptor: TemplatePropertyDescriptor, context: CreatorContext): CreatorProperty<*> { + throw UnsupportedOperationException("No create method has been overridden.") + } +} + class CreatorPropertyFactoryBean : BaseKeyedLazyInstance(), KeyedLazyInstance { Index: src/main/kotlin/creator/custom/types/GradlePluginSelectorCreatorProperty.kt =================================================================== --- src/main/kotlin/creator/custom/types/GradlePluginSelectorCreatorProperty.kt (revision 0ff5c52310324fc559b905ba52aa1496a557ca77) +++ src/main/kotlin/creator/custom/types/GradlePluginSelectorCreatorProperty.kt (revision 8076ef0889f55ee89d9869e5e170f1b6d0ef2c3a) @@ -83,13 +83,32 @@ override fun deserialize(string: String): Holder = Holder.tryParse(string) ?: Holder(SemanticVersion(emptyList()), false) + override fun handleForceValueProperty( + original: Holder, + string: String + ): Holder = original.copy(enabled = string.toBooleanStrictOrNull() ?: original.enabled) + override fun buildUi(panel: Panel) { val label = descriptor.translatedLabel panel.row(label) { - checkBox("") + val checkbox = checkBox("") .bindSelected(enabledProperty) .enabled(descriptor.editable != false) + var previousEnabledStatus = enabledProperty.get() + forceValueProperty?.afterChange { str -> + if (str == null) { + checkbox.enabled(descriptor.editable != false) + enabledProperty.set(previousEnabledStatus) + } else { + str.toBooleanStrictOrNull()?.let { + checkbox.enabled(false) + previousEnabledStatus = enabledProperty.get() + enabledProperty.set(it) + } + } + } + label("Version:").gap(RightGap.SMALL) val combobox = comboBox(versionsModel.get()) .bindItem(versionProperty) @@ -226,8 +245,10 @@ class Factory : CreatorPropertyFactory { override fun create( descriptor: TemplatePropertyDescriptor, - context: CreatorContext - ): CreatorProperty<*> = GradlePluginSelectorCreatorProperty(descriptor, context) + context: CreatorContext, + reporter: TemplateValidationReporter + ): CreatorProperty<*> = + GradlePluginSelectorCreatorProperty(descriptor, context).initForceValueProperty(reporter) } @TemplateApi