This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/master by this push:
     new 6b90267d6e CAUSEWAY-3489: move temporal decomposition logic to value 
semantics
6b90267d6e is described below

commit 6b90267d6e33c98b5ea30fae96c8e13be410d8c0
Author: Andi Huber <[email protected]>
AuthorDate: Fri Mar 15 09:50:32 2024 +0100

    CAUSEWAY-3489: move temporal decomposition logic to value semantics
---
 .../applib/value/semantics/TemporalSupport.java    | 53 +++++++++++++
 .../value/semantics/TemporalValueSemantics.java    |  2 +-
 .../metamodel/facets/object/value/ValueFacet.java  |  5 ++
 .../facets/object/value/ValueFacetAbstract.java    | 30 ++++++--
 .../core/metamodel/object/MmRenderUtils.java       |  4 +-
 .../core/metamodel/object/MmValueUtils.java        | 88 ++++++++++++++++++++++
 .../temporal/LocalDateTimeValueSemantics.java      | 11 +++
 .../temporal/LocalDateValueSemantics.java          | 11 +++
 .../temporal/LocalTimeValueSemantics.java          | 11 +++
 .../temporal/OffsetDateTimeValueSemantics.java     | 14 ++++
 .../temporal/OffsetTimeValueSemantics.java         | 14 ++++
 .../temporal/ZonedDateTimeValueSemantics.java      | 14 ++++
 .../valuetypes/TemporalSemanticsAdapter.java       | 15 +++-
 .../TemporalValueSemanticsProviderTest.java        |  9 +++
 .../ScalarPanelTextFieldWithTemporalPicker.java    |  6 +-
 ...sition.java => TemporalDecompositionModel.java} | 65 +++++++---------
 .../components/scalars/markup/MarkupComponent.java |  2 +-
 .../ui/components/value/StandaloneValuePanel.java  |  2 +-
 18 files changed, 300 insertions(+), 56 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalSupport.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalSupport.java
new file mode 100644
index 0000000000..ff3831a5f8
--- /dev/null
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalSupport.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 org.apache.causeway.applib.value.semantics;
+
+import java.io.Serializable;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.temporal.Temporal;
+import java.util.Optional;
+
+import org.springframework.lang.Nullable;
+
+import org.apache.causeway.commons.functional.Either;
+
+import lombok.Value;
+import lombok.experimental.Accessors;
+
+/**
+ * Decomposes arbitrary time representing values of type T into a canonical 
representation.
+ */
+public interface TemporalSupport<T> extends TemporalCharacteristicsProvider {
+
+    @Value @Accessors(fluent=true) // record candidate
+    public static class TemporalDecomposition implements Serializable {
+        private static final long serialVersionUID = 1L;
+        private final Temporal localTemporal;
+        private final Optional<Either<ZoneId, ZoneOffset>> zoneOrOffset;
+        TemporalCharacteristic temporalCharacteristic;
+        OffsetCharacteristic offsetCharacteristic;
+    }
+
+    /**
+     * Decomposes any temporal value into 2 parts, a 'local' {@link Temporal} 
and a {@link ZoneId}.
+     */
+    Optional<TemporalDecomposition> decomposeTemporal(final @Nullable T 
temporal);
+
+}
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalValueSemantics.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalValueSemantics.java
index b715413488..79c999c32d 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalValueSemantics.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalValueSemantics.java
@@ -39,7 +39,7 @@ extends
     OrderRelation<T, Duration>,
     Parser<T>,
     Renderer<T>,
-    TemporalCharacteristicsProvider {
+    TemporalSupport<T> {
 
     static enum EditingFormatDirection {
 
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacet.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacet.java
index c0206c1f47..c8883a929e 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacet.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacet.java
@@ -29,6 +29,7 @@ import 
org.apache.causeway.applib.value.semantics.DefaultsProvider;
 import org.apache.causeway.applib.value.semantics.OrderRelation;
 import org.apache.causeway.applib.value.semantics.Parser;
 import org.apache.causeway.applib.value.semantics.Renderer;
+import org.apache.causeway.applib.value.semantics.TemporalSupport;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
 import 
org.apache.causeway.applib.value.semantics.ValueSemanticsProvider.Context;
 import org.apache.causeway.commons.collections.Can;
@@ -159,6 +160,10 @@ extends
                 .orElseGet(()->fallbackRenderer(coll.getFeatureIdentifier()));
     }
 
+    // -- TEMPORAL SUPPORT
+
+    Optional<TemporalSupport<T>> selectTemporalSupportForFeature(final 
@Nullable ObjectFeature feature);
+
     // -- COMPOSITE VALUE SUPPORT
 
     Optional<ObjectAction> selectCompositeValueMixinForParameter(
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacetAbstract.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacetAbstract.java
index 7cac0c9ea3..84e475f7d4 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacetAbstract.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacetAbstract.java
@@ -32,9 +32,11 @@ import 
org.apache.causeway.applib.value.semantics.DefaultsProvider;
 import org.apache.causeway.applib.value.semantics.OrderRelation;
 import org.apache.causeway.applib.value.semantics.Parser;
 import org.apache.causeway.applib.value.semantics.Renderer;
+import org.apache.causeway.applib.value.semantics.TemporalSupport;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
 import 
org.apache.causeway.applib.value.semantics.ValueSemanticsProvider.Context;
 import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
@@ -270,6 +272,18 @@ implements ValueFacet<T> {
         return fallbackRenderer(getLogicalType(), featureIdentifier);
     }
 
+    // -- TEMPORAL SUPPORT
+
+    @Override
+    public Optional<TemporalSupport<T>> selectTemporalSupportForFeature(final 
@Nullable ObjectFeature feature) {
+        return (feature!=null
+                ? streamValueSemanticsHonoringQualifiers(feature)
+                : getAllValueSemantics().stream())
+            .filter(TemporalSupport.class::isInstance)
+            
.map(temporalDecomposer->_Casts.<TemporalSupport<T>>uncheckedCast(temporalDecomposer))
+            .findFirst();
+    }
+
     // -- COMPOSITE VALUE SUPPORT
 
     @Override
@@ -320,18 +334,18 @@ implements ValueFacet<T> {
     // -- HELPER
 
     private Stream<ValueSemanticsProvider<T>> 
streamValueSemanticsHonoringQualifiers(
-            final FacetHolder feature) {
+            final @NonNull ObjectFeature feature) {
         return getAllValueSemantics()
-                .stream()
-                .filter(isMatchingAnyOf(qualifiersAccepted(feature)));
+            .stream()
+            .filter(isMatchingAnyOf(qualifiersAccepted(feature)));
     }
 
-    private Can<String> qualifiersAccepted(final FacetHolder feature) {
+    private Can<String> qualifiersAccepted(final @NonNull ObjectFeature 
feature) {
         return feature.lookupFacet(ValueSemanticsSelectingFacet.class)
-        .map(ValueSemanticsSelectingFacet::value)
-        .map(_Strings::emptyToNull)
-        .stream()
-        .collect(Can.toCan());
+                    .map(ValueSemanticsSelectingFacet::value)
+                    .map(_Strings::emptyToNull)
+                    .stream()
+                    .collect(Can.toCan());
     }
 
     private Predicate<ValueSemanticsProvider<T>> isMatchingAnyOf(final 
Can<String> qualifiersAccepted) {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmRenderUtils.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmRenderUtils.java
index e381a30a9e..bf9ab404ee 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmRenderUtils.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmRenderUtils.java
@@ -30,8 +30,8 @@ import lombok.experimental.UtilityClass;
 public class MmRenderUtils {
 
     public String htmlStringForValueType(
-            final @Nullable ManagedObject adapter,
-            final @Nullable ObjectFeature feature) {
+            final @Nullable ObjectFeature feature,
+            final @Nullable ManagedObject adapter) {
 
         if(!ManagedObjects.isSpecified(adapter)) {
             return "";
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmValueUtils.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmValueUtils.java
new file mode 100644
index 0000000000..4c18206279
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmValueUtils.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 org.apache.causeway.core.metamodel.object;
+
+import java.util.Optional;
+
+import org.springframework.lang.Nullable;
+
+import org.apache.causeway.applib.value.semantics.TemporalSupport;
+import 
org.apache.causeway.applib.value.semantics.TemporalSupport.TemporalDecomposition;
+import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
+import org.apache.causeway.core.metamodel.facets.object.value.ValueFacet;
+import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
+
+import lombok.val;
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class MmValueUtils {
+
+    // -- CONTEXT FACTORIES
+
+    //TODO[CAUSEWAY-3489] ever used ?
+    public Optional<ValueSemanticsProvider.Context> 
createValueSemanticsContext(
+            final @Nullable ObjectFeature feature,
+            final @Nullable ObjectSpecification elementType) {
+        return valueFacet(elementType)
+                
.map(valueFacet->valueFacet.createValueSemanticsContext(feature));
+    }
+
+    //TODO[CAUSEWAY-3489] ever used ?
+    public Optional<ValueSemanticsProvider.Context> 
createValueSemanticsContext(
+            final @Nullable ObjectFeature feature,
+            final @Nullable ManagedObject valueObject) {
+        return valueFacet(valueObject)
+                
.map(valueFacet->valueFacet.createValueSemanticsContext(feature));
+    }
+
+    // -- TEMPORAL DECOMPOSITION
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public Optional<TemporalDecomposition> temporalDecomposition(
+            final @Nullable ObjectFeature objectFeature,
+            final @Nullable ManagedObject valueObject) {
+        return valueFacet(valueObject)
+                
.filter(valueFacet->!ManagedObjects.isNullOrUnspecifiedOrEmpty(valueObject))
+                
.flatMap(valueFacet->(Optional<TemporalSupport>)valueFacet.selectTemporalSupportForFeature(objectFeature))
+                .flatMap((final TemporalSupport temporalDecomposer)->{
+                    val pojo = MmUnwrapUtils.single(valueObject);
+                    return temporalDecomposer.decomposeTemporal(pojo);
+                });
+    }
+
+    // -- HELPER
+
+    @SuppressWarnings({ "rawtypes" })
+    private Optional<ValueFacet> valueFacet(final @Nullable 
ObjectSpecification elementType) {
+        return elementType!=null
+                ? elementType.valueFacet()
+                : Optional.empty();
+    }
+
+    @SuppressWarnings({ "rawtypes" })
+    private Optional<ValueFacet> valueFacet(final @Nullable ManagedObject 
valueObject) {
+        if(!ManagedObjects.isSpecified(valueObject)) {
+            return Optional.empty();
+        }
+        return valueFacet(valueObject.getSpecification());
+    }
+
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalDateTimeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalDateTimeValueSemantics.java
index dada946973..995f146977 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalDateTimeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalDateTimeValueSemantics.java
@@ -20,9 +20,11 @@ package 
org.apache.causeway.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
 import java.time.LocalDateTime;
+import java.util.Optional;
 
 import javax.inject.Named;
 
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.applib.value.semantics.OrderRelation;
@@ -57,6 +59,15 @@ implements OrderRelation<LocalDateTime, Duration> {
                 TemporalAdjust::adjustLocalDateTime);
     }
 
+    // -- TEMPORAL DECOMPOSITION
+
+    @Override
+    public Optional<TemporalDecomposition> decomposeTemporal(final @Nullable 
LocalDateTime temporal) {
+        return Optional.ofNullable(temporal)
+                .map(t->new TemporalDecomposition(temporal, Optional.empty(),
+                        temporalCharacteristic, offsetCharacteristic));
+    }
+
     // -- ORDER RELATION
 
     @Override
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalDateValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalDateValueSemantics.java
index 674f472e70..f2d8051159 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalDateValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalDateValueSemantics.java
@@ -20,9 +20,11 @@ package 
org.apache.causeway.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
 import java.time.LocalDate;
+import java.util.Optional;
 
 import javax.inject.Named;
 
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.applib.value.semantics.OrderRelation;
@@ -57,6 +59,15 @@ implements OrderRelation<LocalDate, Duration> {
                 TemporalAdjust::adjustLocalDate);
     }
 
+    // -- TEMPORAL DECOMPOSITION
+
+    @Override
+    public Optional<TemporalDecomposition> decomposeTemporal(final @Nullable 
LocalDate temporal) {
+        return Optional.ofNullable(temporal)
+                .map(t->new TemporalDecomposition(temporal, Optional.empty(),
+                        temporalCharacteristic, offsetCharacteristic));
+    }
+
     // -- ORDER RELATION
 
     @Override
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalTimeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalTimeValueSemantics.java
index f9b27980d2..ca1b7d3728 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalTimeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/LocalTimeValueSemantics.java
@@ -20,9 +20,11 @@ package 
org.apache.causeway.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
 import java.time.LocalTime;
+import java.util.Optional;
 
 import javax.inject.Named;
 
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.commons.collections.Can;
@@ -55,6 +57,15 @@ extends TemporalValueSemanticsProvider<LocalTime> {
                 TemporalAdjust::adjustLocalTime);
     }
 
+    // -- TEMPORAL DECOMPOSITION
+
+    @Override
+    public Optional<TemporalDecomposition> decomposeTemporal(final @Nullable 
LocalTime temporal) {
+        return Optional.ofNullable(temporal)
+                .map(t->new TemporalDecomposition(temporal, Optional.empty(),
+                        temporalCharacteristic, offsetCharacteristic));
+    }
+
     // -- ORDER RELATION
 
     @Override
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/OffsetDateTimeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/OffsetDateTimeValueSemantics.java
index 6abed17061..cf4e83d689 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/OffsetDateTimeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/OffsetDateTimeValueSemantics.java
@@ -20,12 +20,15 @@ package 
org.apache.causeway.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
 import java.time.OffsetDateTime;
+import java.util.Optional;
 
 import javax.inject.Named;
 
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.functional.Either;
 import org.apache.causeway.commons.internal.base._Temporals;
 import org.apache.causeway.schema.common.v2.ValueType;
 
@@ -55,6 +58,17 @@ extends TemporalValueSemanticsProvider<OffsetDateTime> {
                 TemporalAdjust::adjustOffsetDateTime);
     }
 
+    // -- TEMPORAL DECOMPOSITION
+
+    @Override
+    public Optional<TemporalDecomposition> decomposeTemporal(final @Nullable 
OffsetDateTime temporal) {
+        return Optional.ofNullable(temporal)
+                .map(t->new TemporalDecomposition(
+                        temporal.toLocalDateTime(),
+                        Optional.of(Either.right(t.getOffset())),
+                        temporalCharacteristic, offsetCharacteristic));
+    }
+
     // -- ORDER RELATION
 
     @Override
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/OffsetTimeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/OffsetTimeValueSemantics.java
index 4a4b7d9c4d..0b4593fe72 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/OffsetTimeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/OffsetTimeValueSemantics.java
@@ -20,12 +20,15 @@ package 
org.apache.causeway.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
 import java.time.OffsetTime;
+import java.util.Optional;
 
 import javax.inject.Named;
 
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.functional.Either;
 import org.apache.causeway.commons.internal.base._Temporals;
 import org.apache.causeway.schema.common.v2.ValueType;
 
@@ -55,6 +58,17 @@ extends TemporalValueSemanticsProvider<OffsetTime> {
                 TemporalAdjust::adjustOffsetTime);
     }
 
+    // -- TEMPORAL DECOMPOSITION
+
+    @Override
+    public Optional<TemporalDecomposition> decomposeTemporal(final @Nullable 
OffsetTime temporal) {
+        return Optional.ofNullable(temporal)
+                .map(t->new TemporalDecomposition(
+                        temporal.toLocalTime(),
+                        Optional.of(Either.right(t.getOffset())),
+                        temporalCharacteristic, offsetCharacteristic));
+    }
+
     // -- ORDER RELATION
 
     @Override
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/ZonedDateTimeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/ZonedDateTimeValueSemantics.java
index c9593acdec..fdbd616400 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/ZonedDateTimeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/ZonedDateTimeValueSemantics.java
@@ -20,12 +20,15 @@ package 
org.apache.causeway.core.metamodel.valuesemantics.temporal;
 
 import java.time.Duration;
 import java.time.ZonedDateTime;
+import java.util.Optional;
 
 import javax.inject.Named;
 
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.functional.Either;
 import org.apache.causeway.commons.internal.base._Temporals;
 import org.apache.causeway.schema.common.v2.ValueType;
 
@@ -55,6 +58,17 @@ extends TemporalValueSemanticsProvider<ZonedDateTime> {
                 TemporalAdjust::adjustZonedDateTime);
     }
 
+    // -- TEMPORAL DECOMPOSITION
+
+    @Override
+    public Optional<TemporalDecomposition> decomposeTemporal(final @Nullable 
ZonedDateTime temporal) {
+        return Optional.ofNullable(temporal)
+                .map(t->new TemporalDecomposition(
+                        temporal.toLocalDateTime(),
+                        Optional.of(Either.left(t.getZone())),
+                        temporalCharacteristic, offsetCharacteristic));
+    }
+
     // -- ORDER RELATION
 
     @Override
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuetypes/TemporalSemanticsAdapter.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuetypes/TemporalSemanticsAdapter.java
index a6e265fd73..3d35e10af1 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuetypes/TemporalSemanticsAdapter.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuetypes/TemporalSemanticsAdapter.java
@@ -20,12 +20,25 @@ package org.apache.causeway.core.metamodel.valuetypes;
 
 import java.time.Duration;
 import java.time.temporal.Temporal;
+import java.util.Optional;
+
+import org.springframework.lang.Nullable;
 
 import 
org.apache.causeway.applib.value.semantics.TemporalCharacteristicsProvider;
+import org.apache.causeway.applib.value.semantics.TemporalSupport;
 
 public abstract class TemporalSemanticsAdapter<T, D extends Temporal>
 extends ValueSemanticsAdapter<T, D, Duration>
-implements TemporalCharacteristicsProvider {
+implements
+    TemporalSupport<T> {
+
+    // -- TEMPORAL DECOMPOSITION
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public final Optional<TemporalDecomposition> decomposeTemporal(final 
@Nullable T value) {
+        return 
((TemporalSupport<D>)getDelegate()).decomposeTemporal(toDelegateValue(value));
+    }
 
     // -- ORDER RELATION
 
diff --git 
a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProviderTest.java
 
b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProviderTest.java
index 4567b585cd..cc9bdd0bcb 100644
--- 
a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProviderTest.java
+++ 
b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/temporal/TemporalValueSemanticsProviderTest.java
@@ -22,6 +22,7 @@ import java.time.Duration;
 import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.Temporal;
+import java.util.Optional;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -29,6 +30,8 @@ import org.junit.jupiter.params.provider.EnumSource;
 
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
+import org.springframework.lang.Nullable;
+
 import org.apache.causeway.applib.annotation.TimePrecision;
 import org.apache.causeway.applib.locale.UserLocale;
 import 
org.apache.causeway.applib.value.semantics.TemporalCharacteristicsProvider.OffsetCharacteristic;
@@ -37,6 +40,7 @@ import 
org.apache.causeway.applib.value.semantics.TemporalValueSemantics;
 import 
org.apache.causeway.applib.value.semantics.TemporalValueSemantics.EditingFormatDirection;
 import 
org.apache.causeway.applib.value.semantics.TemporalValueSemantics.TemporalEditingPattern;
 import 
org.apache.causeway.applib.value.semantics.ValueSemanticsProvider.Context;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.schema.common.v2.ValueType;
 
@@ -108,6 +112,11 @@ class TemporalValueSemanticsProviderTest {
             return super.getTemporalEditingFormat(context, 
temporalCharacteristic, offsetCharacteristic, timePrecision, direction,
                     editingPattern); }
 
+        @Override
+        public Optional<TemporalDecomposition> decomposeTemporal(final 
@Nullable Temporal temporal) {
+            throw _Exceptions.notImplemented(); // method not tested yet
+        }
+
     }
 
 }
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
index 8cb8ee6e0d..18c616436a 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
@@ -35,7 +35,7 @@ import org.apache.causeway.core.metamodel.util.Facets;
 import org.apache.causeway.viewer.wicket.model.models.ScalarModel;
 import 
org.apache.causeway.viewer.wicket.model.value.ConverterBasedOnValueSemantics;
 import 
org.apache.causeway.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.InputFragment;
-import 
org.apache.causeway.viewer.wicket.ui.components.scalars.datepicker.TemporalDecomposition;
+import 
org.apache.causeway.viewer.wicket.ui.components.scalars.datepicker.TemporalDecompositionModel;
 import 
org.apache.causeway.viewer.wicket.ui.components.scalars.datepicker.TextFieldWithDateTimePicker;
 import 
org.apache.causeway.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
 import org.apache.causeway.viewer.wicket.ui.util.Wkt;
@@ -51,7 +51,7 @@ extends ScalarPanelTextFieldWithValueSemantics<T>  {
 
     private static final long serialVersionUID = 1L;
 
-    private TemporalDecomposition<T> temporalDecomposition;
+    private TemporalDecompositionModel<T> temporalDecomposition;
 
     public ScalarPanelTextFieldWithTemporalPicker(
             final String id, final ScalarModel scalarModel, final Class<T> 
type) {
@@ -66,7 +66,7 @@ extends ScalarPanelTextFieldWithValueSemantics<T>  {
     protected final TextField<T> createTextField(final String id) {
         val scalarModel = scalarModel();
 
-        this.temporalDecomposition = TemporalDecomposition.create(type,
+        this.temporalDecomposition = TemporalDecompositionModel.create(type,
                 scalarModel,
                 offsetCharacteristic(),
                 (ConverterBasedOnValueSemantics<T>)converterElseFail());
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TemporalDecomposition.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TemporalDecompositionModel.java
similarity index 74%
rename from 
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TemporalDecomposition.java
rename to 
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TemporalDecompositionModel.java
index 869ea5f4a4..ab0eec9bdd 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TemporalDecomposition.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TemporalDecompositionModel.java
@@ -19,11 +19,8 @@
 package org.apache.causeway.viewer.wicket.ui.components.scalars.datepicker;
 
 import java.time.Instant;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
 import java.time.temporal.Temporal;
 import java.util.Locale;
 
@@ -32,13 +29,17 @@ import org.apache.wicket.util.convert.IConverter;
 
 import org.apache.causeway.applib.services.iactnlayer.InteractionContext;
 import 
org.apache.causeway.applib.value.semantics.TemporalCharacteristicsProvider.OffsetCharacteristic;
+import 
org.apache.causeway.applib.value.semantics.TemporalSupport.TemporalDecomposition;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsAbstract;
+import org.apache.causeway.commons.functional.Either;
 import org.apache.causeway.commons.internal.assertions._Assert;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.base._Temporals;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.commons.ViewOrEditMode;
 import org.apache.causeway.core.metamodel.interactions.managed.ManagedValue;
-import org.apache.causeway.core.metamodel.object.MmUnwrapUtils;
+import org.apache.causeway.core.metamodel.object.MmValueUtils;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 import org.apache.causeway.viewer.wicket.model.models.ScalarModel;
 import 
org.apache.causeway.viewer.wicket.model.value.ConverterBasedOnValueSemantics;
 
@@ -53,10 +54,10 @@ import lombok.Setter;
  * based on existing widgets, that do not support zone or offset information.
  */
 @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
-public class TemporalDecomposition<T> implements IConverter<T> {
+public class TemporalDecompositionModel<T> implements IConverter<T> {
     private static final long serialVersionUID = 1L;
 
-    public static <T extends Temporal> TemporalDecomposition<T> create(final 
Class<T> type,
+    public static <T extends Temporal> TemporalDecompositionModel<T> 
create(final Class<T> type,
             final ScalarModel scalarModel,
             final OffsetCharacteristic offsetCharacteristic,
             final ConverterBasedOnValueSemantics<T> fullConverter) {
@@ -77,7 +78,7 @@ public class TemporalDecomposition<T> implements 
IConverter<T> {
                         .orElse(ZoneOffset.UTC)
                 : ZoneOffset.UTC; // not used
 
-        var tempDecomp = new TemporalDecomposition<>(type,
+        var tempDecomp = new TemporalDecompositionModel<>(type,
                 offsetCharacteristic,
                 userZoneId,
                 fullConverter,
@@ -85,7 +86,7 @@ public class TemporalDecomposition<T> implements 
IConverter<T> {
                 editingPattern);
 
         if(needsDecomposition) {
-            tempDecomp.initFrom(scalarModel.proposedValue());
+            tempDecomp.initFrom(scalarModel.getMetaModel(), 
scalarModel.proposedValue());
         }
         return tempDecomp;
     }
@@ -99,37 +100,23 @@ public class TemporalDecomposition<T> implements 
IConverter<T> {
     @Getter
     private final String editingPattern;
 
-    /**
-     * @implNote only supports zoned types as known at the time of writing
-     */
-    private void initFrom(final ManagedValue proposedValue) {
-        var temporalValue = 
MmUnwrapUtils.single(proposedValue.getValue().getValue());
-        if(temporalValue instanceof ZonedDateTime) {
-            var zonedDateTime = (ZonedDateTime) temporalValue;
-            this.zoneId = zonedDateTime.getZone();
-        } else if(temporalValue instanceof OffsetDateTime) {
-            var offsetDateTime = (OffsetDateTime) temporalValue;
-            this.zoneOffset = offsetDateTime.getOffset();
-        } else if(temporalValue instanceof OffsetTime) {
-            var offsetTime = (OffsetTime) temporalValue;
-            this.zoneOffset = offsetTime.getOffset();
-        } else if(temporalValue instanceof org.joda.time.DateTime) {
-            var jodaDateTime = (org.joda.time.DateTime) temporalValue;
-            this.zoneId = jodaDateTime.getZone().toTimeZone().toZoneId();
-        } else {
-            // either temporalValue is null or unsupported
-            switch (offsetCharacteristic) {
-            case OFFSET:
-                this.zoneOffset = 
userZoneId.getRules().getOffset(Instant.now());
-                break;
-            case ZONED:
-                this.zoneId = userZoneId;
-                break;
-            case LOCAL:
-            default:
-                break;
-            }
-        }
+    private void initFrom(final ObjectFeature objectFeature, final 
ManagedValue proposedValue) {
+        var temporalValue = proposedValue.getValue().getValue();
+        var zoneOrOffset = MmValueUtils.temporalDecomposition(objectFeature, 
temporalValue)
+            .flatMap(TemporalDecomposition::zoneOrOffset)
+            .orElseGet(()->{
+                // either temporalValue is null, empty or unsupported
+                switch (offsetCharacteristic) {
+                case ZONED:
+                    return Either.left(userZoneId);
+                case OFFSET:
+                    return 
Either.right(userZoneId.getRules().getOffset(Instant.now()));
+                case LOCAL:
+                default:
+                    throw _Exceptions.unexpectedCodeReach();
+                }
+            });
+        zoneOrOffset.accept(this::setZoneId, this::setZoneOffset);
     }
 
     // -- CONVERTER
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
index 7da8838098..d564ab7333 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
@@ -106,7 +106,7 @@ public class MarkupComponent extends WebComponent {
         if(modelObject instanceof ManagedObject) {
             val adapter = (ManagedObject) modelObject;
             val feature = 
lookupObjectFeatureIn(getDefaultModel()).orElse(null);
-            val asHtml = MmRenderUtils.htmlStringForValueType(adapter, 
feature);
+            val asHtml = MmRenderUtils.htmlStringForValueType(feature, 
adapter);
             return asHtml != null
                 ? asHtml
                 : fallback;
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/value/StandaloneValuePanel.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/value/StandaloneValuePanel.java
index e76216b8a9..9b2e2c31d7 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/value/StandaloneValuePanel.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/value/StandaloneValuePanel.java
@@ -51,7 +51,7 @@ extends PanelAbstract<ManagedObject, ValueModel> {
         // (we probably need to remove StandaloneValuePanel and utilize the 
ScalarPanel for standalone values instead)
         
if(isProbablySimpleInlineHtml(valueModel.getObjectMember().getElementType())) {
             Wkt.markupAdd(this, ID_STANDALONE_VALUE, ()->
-                MmRenderUtils.htmlStringForValueType(getModel().getObject(), 
getModel().getObjectMember())
+                
MmRenderUtils.htmlStringForValueType(getModel().getObjectMember(), 
getModel().getObject())
             );
         } else {
             // resort to (textual) title rendering


Reply via email to