|
| 1 | +/* |
| 2 | + * Copyright 2019-2023 JetBrains s.r.o. and contributors. |
| 3 | + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. |
| 4 | + */ |
| 5 | + |
| 6 | +package kotlinx.datetime.internal.format |
| 7 | + |
| 8 | +import kotlinx.datetime.internal.format.parser.AssignableField |
| 9 | +import kotlin.reflect.KMutableProperty1 |
| 10 | + |
| 11 | +/** |
| 12 | + * A lens-like interface for accessing fields of objects. |
| 13 | + */ |
| 14 | +internal interface Accessor<in Object, Field>: AssignableField<Object, Field> { |
| 15 | + /** |
| 16 | + * The value of the field in the given object, or `null` if the field is not set. |
| 17 | + */ |
| 18 | + fun getter(container: Object): Field? |
| 19 | + |
| 20 | + /** |
| 21 | + * Function that returns the value of the field in the given object, |
| 22 | + * or throws [IllegalStateException] if the field is not set. |
| 23 | + * |
| 24 | + * This function is used to access fields during formatting. |
| 25 | + */ |
| 26 | + fun getterNotNull(container: Object): Field = |
| 27 | + getter(container) ?: throw IllegalStateException("Field $name is not set") |
| 28 | +} |
| 29 | + |
| 30 | +/** |
| 31 | + * An implementation of [Accessor] for a mutable property of an object. |
| 32 | + */ |
| 33 | +internal class PropertyAccessor<Object, Field>(private val property: KMutableProperty1<Object, Field?>): Accessor<Object, Field> { |
| 34 | + override val name: String get() = property.name |
| 35 | + |
| 36 | + override fun trySetWithoutReassigning(container: Object, newValue: Field): Field? { |
| 37 | + val oldValue = property.get(container) |
| 38 | + return when { |
| 39 | + oldValue === null -> { |
| 40 | + property.set(container, newValue) |
| 41 | + null |
| 42 | + } |
| 43 | + oldValue == newValue -> null |
| 44 | + else -> oldValue |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + override fun getter(container: Object): Field? = property.get(container) |
| 49 | +} |
| 50 | + |
| 51 | +/** |
| 52 | + * A description of the field's numeric sign. |
| 53 | + * |
| 54 | + * Several fields can share the same sign. For example, the hour and the minute of the UTC offset have the same sign, |
| 55 | + * and setting the sign of the hour also sets the sign of the minute. |
| 56 | + * |
| 57 | + * Implementations of this interface are *not* required to redefine [equals] and [hashCode]. |
| 58 | + * Instead, signs should be defined as singletons and compared by reference. |
| 59 | + */ |
| 60 | +internal interface FieldSign<in Target> { |
| 61 | + /** |
| 62 | + * The field that is `true` if the value of the field is known to be negative, and `false` otherwise. |
| 63 | + * Can be both read from and written to. |
| 64 | + */ |
| 65 | + val isNegative: Accessor<Target, Boolean> |
| 66 | + |
| 67 | + /** |
| 68 | + * A check for whether the current value of the field is zero. |
| 69 | + */ |
| 70 | + fun isZero(obj: Target): Boolean |
| 71 | +} |
| 72 | + |
| 73 | +/** |
| 74 | + * A specification of a field. |
| 75 | + * |
| 76 | + * Fields represent parts of objects, regardless of how they are stored or formatted. |
| 77 | + * For example, a field "day of week" can be represented with both strings of various kinds ("Monday", "Mon") and |
| 78 | + * numbers ("1" for Monday, "2" for Tuesday, etc.), but the field itself is the same. |
| 79 | + * |
| 80 | + * Fields can typically contain `null` values, which means that the field is not set. |
| 81 | + */ |
| 82 | +internal interface FieldSpec<in Target, Type> { |
| 83 | + /** |
| 84 | + * The function with which the field can be accessed. |
| 85 | + */ |
| 86 | + val accessor: Accessor<Target, Type> |
| 87 | + |
| 88 | + /** |
| 89 | + * The default value of the field, or `null` if the field has none. |
| 90 | + */ |
| 91 | + val defaultValue: Type? |
| 92 | + |
| 93 | + /** |
| 94 | + * The name of the field. |
| 95 | + */ |
| 96 | + val name: String |
| 97 | + |
| 98 | + /** |
| 99 | + * The sign corresponding to the field value, or `null` if the field has none. |
| 100 | + */ |
| 101 | + val sign: FieldSign<Target>? |
| 102 | +} |
| 103 | + |
| 104 | +/** |
| 105 | + * Inherit from this class to obtain a sensible [toString] implementation for debugging. |
| 106 | + */ |
| 107 | +internal abstract class AbstractFieldSpec<in Target, Type>: FieldSpec<Target, Type> { |
| 108 | + override fun toString(): String = "The field $name (default value is $defaultValue)" |
| 109 | +} |
| 110 | + |
| 111 | +/** |
| 112 | + * A specification of a field that can contain values of any kind. |
| 113 | + * Used for fields additional information about which is not that important for parsing/formatting. |
| 114 | + */ |
| 115 | +internal class GenericFieldSpec<in Target, Type>( |
| 116 | + override val accessor: Accessor<Target, Type>, |
| 117 | + override val name: String = accessor.name, |
| 118 | + override val defaultValue: Type? = null, |
| 119 | + override val sign: FieldSign<Target>? = null, |
| 120 | +) : AbstractFieldSpec<Target, Type>() |
| 121 | + |
| 122 | +/** |
| 123 | + * A specification of a field that can only contain non-negative numeric values. |
| 124 | + */ |
| 125 | +internal class UnsignedFieldSpec<in Target>( |
| 126 | + override val accessor: Accessor<Target, Int>, |
| 127 | + /** |
| 128 | + * The minimum value of the field. |
| 129 | + */ |
| 130 | + val minValue: Int, |
| 131 | + /** |
| 132 | + * The maximum value of the field. |
| 133 | + */ |
| 134 | + val maxValue: Int, |
| 135 | + override val name: String = accessor.name, |
| 136 | + override val defaultValue: Int? = null, |
| 137 | + override val sign: FieldSign<Target>? = null, |
| 138 | +) : AbstractFieldSpec<Target, Int>() { |
| 139 | + /** |
| 140 | + * The maximum length of the field when represented as a decimal number. |
| 141 | + */ |
| 142 | + val maxDigits: Int = when { |
| 143 | + maxValue < 10 -> 1 |
| 144 | + maxValue < 100 -> 2 |
| 145 | + maxValue < 1000 -> 3 |
| 146 | + else -> throw IllegalArgumentException("Max value $maxValue is too large") |
| 147 | + } |
| 148 | +} |
0 commit comments