From 9fd89a5b60f1d6fe6daaef966c696ce36711d60f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 18 Mar 2019 20:34:06 +0300 Subject: [PATCH] Descriptors --- .../dataforge/descriptors/NodeDescriptor.kt | 1 - .../dataforge/descriptors/ValueDescriptor.kt | 183 ++++++++---------- .../hep/dataforge/descriptors/annotations.kt | 149 ++++++++++++++ .../hep/dataforge/meta/ConfigDelegates.kt | 21 +- .../kotlin/hep/dataforge/meta/Delegates.kt | 3 + 5 files changed, 245 insertions(+), 112 deletions(-) create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/annotations.kt diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/NodeDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/NodeDescriptor.kt index dd6532a0..486319db 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/NodeDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/NodeDescriptor.kt @@ -22,7 +22,6 @@ package hep.dataforge.descriptors import hep.dataforge.Named -import hep.dataforge.description.ValueDescriptor import hep.dataforge.meta.* import hep.dataforge.names.Name import java.util.* diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/ValueDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/ValueDescriptor.kt index 4ee4e351..3416b55b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/ValueDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/ValueDescriptor.kt @@ -14,19 +14,10 @@ * limitations under the License. */ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package hep.dataforge.description +package hep.dataforge.descriptors -import hep.dataforge.Named import hep.dataforge.meta.* -import hep.dataforge.names.AnonymousNotAlowed -import hep.dataforge.values.BooleanValue -import hep.dataforge.values.Value -import hep.dataforge.values.ValueType +import hep.dataforge.values.* /** * A descriptor for meta value @@ -35,54 +26,54 @@ import hep.dataforge.values.ValueType * * @author Alexander Nozik */ -class ValueDescriptor(val meta: Meta) : MetaRepr { +class ValueDescriptor(override val config: Config) : Specification { /** * The default for this value. Null if there is no default. * * @return */ - val default by meta.value() + var default: Value? by value() /** * True if multiple values with this name are allowed. * * @return */ - val multiple: Boolean by meta.boolean(false) + var multiple: Boolean by boolean(false) /** * True if the value is required * * @return */ - val required: Boolean by meta.boolean(default == null) + var required: Boolean by boolean { default == null } /** * Value name * * @return */ - val name: String by meta.string{ error("Anonimous descriptors are not allowed")} + var name: String by string { error("Anonymous descriptors are not allowed") } /** * The value info * * @return */ - val info: String by stringValue(def = "") + var info: String? by string() /** * A list of allowed ValueTypes. Empty if any value type allowed * * @return */ - val type: List by customValue(def = emptyList()) { - it.list.map { v -> ValueType.valueOf(v.string) } + var type: List by value().map { + it?.list?.map { v -> ValueType.valueOf(v.string) } ?: emptyList() } - val tags: List by customValue(def = emptyList()) { - meta.getStringArray("tags").toList() + var tags: List by value().map { value -> + value?.list?.map { it.string } ?: emptyList() } /** @@ -92,7 +83,7 @@ class ValueDescriptor(val meta: Meta) : MetaRepr { * @param value * @return */ - fun isValueAllowed(value: Value): Boolean { + fun isAllowedValue(value: Value): Boolean { return (type.isEmpty() || type.contains(ValueType.STRING) || type.contains(value.type)) && (allowedValues.isEmpty() || allowedValues.contains( value )) @@ -104,96 +95,74 @@ class ValueDescriptor(val meta: Meta) : MetaRepr { * * @return */ - val allowedValues: List by customValue( - def = if (type.size == 1 && type[0] === ValueType.BOOLEAN) { - listOf(BooleanValue.TRUE, BooleanValue.FALSE) + var allowedValues: List by value().map { + it?.list ?: if (type.size == 1 && type[0] === ValueType.BOOLEAN) { + listOf(True, False) } else { emptyList() } - ) { it.list } + } - companion object { + companion object : SpecificationCompanion { - /** - * Build a value descriptor from annotation - */ - fun build(def: ValueDef): ValueDescriptor { - val builder = MetaBuilder("value") - .setValue("name", def.key) + override fun wrap(config: Config): ValueDescriptor = ValueDescriptor(config) - if (def.type.isNotEmpty()) { - builder.setValue("type", def.type) - } - - if (def.multiple) { - builder.setValue("multiple", def.multiple) - } - - if (!def.info.isEmpty()) { - builder.setValue("info", def.info) - } - - if (def.allowed.isNotEmpty()) { - builder.setValue("allowedValues", def.allowed) - } else if (def.enumeration != Any::class) { - if (def.enumeration.java.isEnum) { - val values = def.enumeration.java.enumConstants - builder.setValue("allowedValues", values.map { it.toString() }) - } else { - throw RuntimeException("Only enumeration classes are allowed in 'enumeration' annotation property") - } - } - - if (def.def.isNotEmpty()) { - builder.setValue("default", def.def) - } else if (!def.required) { - builder.setValue("required", def.required) - } - - if (def.tags.isNotEmpty()) { - builder.setValue("tags", def.tags) - } - return ValueDescriptor(builder) - } - - /** - * Build a value descriptor from its fields - */ - fun build( - name: String, - info: String = "", - defaultValue: Any? = null, - required: Boolean = false, - multiple: Boolean = false, - types: List = emptyList(), - allowedValues: List = emptyList() - ): ValueDescriptor { - val valueBuilder = buildMeta("value") { - "name" to name - if (!types.isEmpty()) "type" to types - if (required) "required" to required - if (multiple) "multiple" to multiple - if (!info.isEmpty()) "info" to info - if (defaultValue != null) "default" to defaultValue - if (!allowedValues.isEmpty()) "allowedValues" to allowedValues - }.build() - return ValueDescriptor(valueBuilder) - } - - /** - * Build empty value descriptor - */ - fun empty(valueName: String): ValueDescriptor { - val builder = MetaBuilder("value") - .setValue("name", valueName) - return ValueDescriptor(builder) - } - - /** - * Merge two separate value descriptors - */ - fun merge(primary: ValueDescriptor, secondary: ValueDescriptor): ValueDescriptor { - return ValueDescriptor(Laminate(primary.meta, secondary.meta)) - } +// /** +// * Build a value descriptor from annotation +// */ +// fun build(def: ValueDef): ValueDescriptor { +// val builder = MetaBuilder("value") +// .setValue("name", def.key) +// +// if (def.type.isNotEmpty()) { +// builder.setValue("type", def.type) +// } +// +// if (def.multiple) { +// builder.setValue("multiple", def.multiple) +// } +// +// if (!def.info.isEmpty()) { +// builder.setValue("info", def.info) +// } +// +// if (def.allowed.isNotEmpty()) { +// builder.setValue("allowedValues", def.allowed) +// } else if (def.enumeration != Any::class) { +// if (def.enumeration.java.isEnum) { +// val values = def.enumeration.java.enumConstants +// builder.setValue("allowedValues", values.map { it.toString() }) +// } else { +// throw RuntimeException("Only enumeration classes are allowed in 'enumeration' annotation property") +// } +// } +// +// if (def.def.isNotEmpty()) { +// builder.setValue("default", def.def) +// } else if (!def.required) { +// builder.setValue("required", def.required) +// } +// +// if (def.tags.isNotEmpty()) { +// builder.setValue("tags", def.tags) +// } +// return ValueDescriptor(builder) +// } +// +// /** +// * Build empty value descriptor +// */ +// fun empty(valueName: String): ValueDescriptor { +// val builder = MetaBuilder("value") +// .setValue("name", valueName) +// return ValueDescriptor(builder) +// } +// +// /** +// * Merge two separate value descriptors +// */ +// fun merge(primary: ValueDescriptor, secondary: ValueDescriptor): ValueDescriptor { +// return ValueDescriptor(Laminate(primary.meta, secondary.meta)) +// } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/annotations.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/annotations.kt new file mode 100644 index 00000000..25349ea7 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/annotations.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2018 Alexander Nozik. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hep.dataforge.descriptors + +import hep.dataforge.values.ValueType +import kotlin.reflect.KClass + +@Target(AnnotationTarget.PROPERTY) +@MustBeDocumented +annotation class ValueDef( + val key: String, + val type: Array = [ValueType.STRING], + val multiple: Boolean = false, + val def: String = "", + val info: String = "", + val required: Boolean = true, + val allowed: Array = [], + val enumeration: KClass<*> = Any::class, + val tags: Array = [] +) + +@MustBeDocumented +annotation class NodeDef( + val key: String, + val info: String = "", + val multiple: Boolean = false, + val required: Boolean = false, + val tags: Array = [], + /** + * A list of child value descriptors + */ + val values: Array = [], + /** + * A target class for this node to describe + * @return + */ + val type: KClass<*> = Any::class, + /** + * The DataForge path to the resource containing the description. Following targets are supported: + * + * 1. resource + * 1. file + * 1. class + * 1. method + * 1. property + * + * + * Does not work if [type] is provided + * + * @return + */ + val descriptor: String = "" +) + +/** + * Description text for meta property, node or whole object + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class Description(val value: String) + +/** + * Annotation for value property which states that lists are expected + */ +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class Multiple + +/** + * Descriptor target + * The DataForge path to the resource containing the description. Following targets are supported: + * 1. resource + * 1. file + * 1. class + * 1. method + * 1. property + * + * + * Does not work if [type] is provided + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class Descriptor(val value: String) + + +/** + * Aggregator class for descriptor nodes + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class DescriptorNodes(vararg val nodes: NodeDef) + +/** + * Aggregator class for descriptor values + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class DescriptorValues(vararg val nodes: ValueDef) + +/** + * Alternative name for property descriptor declaration + */ +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class DescriptorName(val name: String) + +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class DescriptorValue(val def: ValueDef) +//TODO enter fields directly? + +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class ValueProperty( + val name: String = "", + val type: Array = [ValueType.STRING], + val multiple: Boolean = false, + val def: String = "", + val enumeration: KClass<*> = Any::class, + val tags: Array = [] +) + + +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class NodeProperty(val name: String = "") diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigDelegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigDelegates.kt index 581fa7ed..8e717948 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigDelegates.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigDelegates.kt @@ -10,8 +10,8 @@ import kotlin.jvm.JvmName /** * A property delegate that uses custom key */ -fun Configurable.value(default: Value = Null, key: String? = null) = - ValueConfigDelegate(config, key, default) +fun Configurable.value(default: Any = Null, key: String? = null) = + ValueConfigDelegate(config, key, Value.of(default)) fun Configurable.string(default: String? = null, key: String? = null) = StringConfigDelegate(config, key, default) @@ -28,15 +28,28 @@ fun Configurable.child(key: String? = null) = MetaNodeDelegate(config, key) @JvmName("safeString") fun Configurable.string(default: String, key: String? = null) = - SafeStringConfigDelegate(config, key, default) + SafeStringConfigDelegate(config, key) { default } @JvmName("safeBoolean") fun Configurable.boolean(default: Boolean, key: String? = null) = - SafeBooleanConfigDelegate(config, key, default) + SafeBooleanConfigDelegate(config, key) { default } @JvmName("safeNumber") fun Configurable.number(default: Number, key: String? = null) = + SafeNumberConfigDelegate(config, key) { default } + +@JvmName("safeString") +fun Configurable.string(key: String? = null, default: () -> String) = + SafeStringConfigDelegate(config, key, default) + +@JvmName("safeBoolean") +fun Configurable.boolean(key: String? = null, default: () -> Boolean) = + SafeBooleanConfigDelegate(config, key, default) + +@JvmName("safeNumber") +fun Configurable.number(key: String? = null, default: () -> Number) = SafeNumberConfigDelegate(config, key, default) + inline fun > Configurable.enum(default: E, key: String? = null) = SafeEnumvConfigDelegate(config, key, default) { enumValueOf(it) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt index 5a6c8aea..5a7e845d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt @@ -183,6 +183,9 @@ class ValueConfigDelegate>( config.setValue(name, value) } } + + fun map(writer: (T) -> Value? = { Value.of(it) }, reader: (Value?) -> T) = + ReadWriteDelegateWrapper(this, reader, writer) } class StringConfigDelegate>(