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

ahuber pushed a commit to branch 3489-temporal.editing.w.tz
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/3489-temporal.editing.w.tz by 
this push:
     new cc84f51394 CAUSEWAY-3489: introduces TemporalDecomposition
cc84f51394 is described below

commit cc84f51394ca1dd72fc9d413cfafbb853995286d
Author: Andi Huber <[email protected]>
AuthorDate: Wed Mar 13 15:41:55 2024 +0100

    CAUSEWAY-3489: introduces TemporalDecomposition
    
    - to handle temporal values with zone or offset information based on
    existing widgets, that do not support zone or offset information
---
 .../value/semantics/TemporalValueSemantics.java    |  18 +--
 .../scalars/ScalarPanelFormFieldAbstract.html      |   4 +-
 .../scalars/ScalarPanelTextFieldAbstract.java      |   6 +
 .../ScalarPanelTextFieldWithTemporalPicker.java    |  87 +++++++-----
 .../scalars/datepicker/TemporalDecomposition.java  | 157 +++++++++++++++++++++
 .../datepicker/TextFieldWithDateTimePicker.java    |  18 +--
 .../apache/causeway/viewer/wicket/ui/util/Wkt.java |  14 +-
 .../ComponentFactoryRegistrarDefault.java          |   7 +-
 8 files changed, 249 insertions(+), 62 deletions(-)

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 58cd36e0ad..7709ad1883 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
@@ -360,9 +360,9 @@ extends
      */
     default List<ZoneId> getAvailableZoneIds() {
         return ZoneId.getAvailableZoneIds().stream()
-                .sorted()
-                .map(ZoneId::of)
-                .collect(Collectors.toList());
+            .sorted()
+            .map(ZoneId::of)
+            .collect(Collectors.toList());
     }
 
     /**
@@ -370,12 +370,12 @@ extends
      */
     default List<ZoneOffset> getAvailableOffsets() {
         val now = LocalDateTime.now();
-        return ZoneId.getAvailableZoneIds().stream()
-                .map(ZoneId::of)
-                .map(ZoneId::getRules)
-                
.flatMap(zoneIdRules->zoneIdRules.getValidOffsets(now).stream())
-                .sorted()
-                .collect(Collectors.toList());
+        return getAvailableZoneIds().stream()
+            .map(ZoneId::getRules)
+            .flatMap(zoneIdRules->zoneIdRules.getValidOffsets(now).stream())
+            .sorted()
+            .distinct()
+            .collect(Collectors.toList());
     }
 
 }
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelFormFieldAbstract.html
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelFormFieldAbstract.html
index 0c76f8cda7..32317d95ae 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelFormFieldAbstract.html
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelFormFieldAbstract.html
@@ -157,7 +157,7 @@
                        <input wicket:id="scalarValue"
                                type="text" name="scalarValue"
                                class="form-control form-control-sm scalarValue 
fragment-input-date"/>
-                       <div class="input-group">
+                       <div class="input-group input-group-sm">
                            <span class="input-group-text">Offset:</span>
                                <select class="form-control" 
wicket:id="timeoffset" name="timeoffset" data-noreset="true" style="width: 
auto;" />
                        </div>  
@@ -167,7 +167,7 @@
                        <input wicket:id="scalarValue"
                                type="text" name="scalarValue"
                                class="form-control form-control-sm scalarValue 
fragment-input-date"/>
-                       <div class="input-group">
+                       <div class="input-group input-group-sm">
                            <span class="input-group-text">Zone:</span>
                                <select class="form-control" 
wicket:id="timezone" name="timezone" data-noreset="true" style="width: auto;" />
                        </div>  
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
index e21130573d..2824b22b52 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
@@ -26,6 +26,7 @@ import org.apache.wicket.model.IModel;
 import org.apache.wicket.util.convert.IConverter;
 
 import org.apache.causeway.commons.internal.assertions._Assert;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.viewer.commons.model.components.UiString;
 import org.apache.causeway.viewer.wicket.model.models.ScalarModel;
 import 
org.apache.causeway.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.InputFragment;
@@ -66,6 +67,11 @@ extends ScalarPanelFormFieldAbstract<T> {
      */
     protected abstract Optional<IConverter<T>> converter();
 
+    protected final IConverter<T> converterElseFail() {
+        return converter().orElseThrow(()->
+            _Exceptions.illegalState("framework bug: %s requires a converter", 
this.getClass().getSimpleName()));
+    }
+
     // --
 
     /**
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 4ec5611bc3..bd066180a6 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
@@ -20,6 +20,7 @@ package 
org.apache.causeway.viewer.wicket.ui.components.scalars;
 
 import java.time.ZoneId;
 import java.time.ZoneOffset;
+import java.time.temporal.Temporal;
 import java.util.Optional;
 
 import org.apache.wicket.MarkupContainer;
@@ -30,27 +31,31 @@ import org.apache.wicket.model.PropertyModel;
 
 import org.apache.causeway.applib.value.semantics.TemporalValueSemantics;
 import 
org.apache.causeway.applib.value.semantics.TemporalValueSemantics.OffsetCharacteristic;
+import 
org.apache.causeway.applib.value.semantics.TemporalValueSemantics.TemporalCharacteristic;
+import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 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.TextFieldWithDateTimePicker;
 import 
org.apache.causeway.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
 import org.apache.causeway.viewer.wicket.ui.util.Wkt;
 import org.apache.causeway.viewer.wicket.ui.util.WktComponents;
 
-import lombok.Getter;
-import lombok.Setter;
 import lombok.val;
 
 /**
  * Panel for rendering scalars representing dates, along with a date picker.
  */
-public class ScalarPanelTextFieldWithTemporalPicker<T>
+public class ScalarPanelTextFieldWithTemporalPicker<T extends Temporal>
 extends ScalarPanelTextFieldWithValueSemantics<T>  {
 
     private static final long serialVersionUID = 1L;
 
+    private TemporalDecomposition<T> temporalDecomposition;
+
     public ScalarPanelTextFieldWithTemporalPicker(
             final String id, final ScalarModel scalarModel, final Class<T> 
type) {
         super(id, scalarModel, type);
@@ -63,38 +68,23 @@ extends ScalarPanelTextFieldWithValueSemantics<T>  {
     @Override
     protected final TextField<T> createTextField(final String id) {
         val scalarModel = scalarModel();
-        val converter = converter().orElseThrow(()->
-            _Exceptions.illegalArgument("framework bug: 
ScalarPanelTextFieldWithTemporalPicker requires a converter"));
 
-        val textField = new TextFieldWithDateTimePicker<T>(
-                id, scalarModel, type, converter);
+        this.temporalDecomposition = TemporalDecomposition.create(type,
+                scalarModel,
+                temporalValueSemantics(),
+                (ConverterBasedOnValueSemantics<T>)converterElseFail());
 
-        /* [CAUSEWAY-3201]
-         * Adding OnChangeAjaxBehavior registers a JavaScript event listener 
on change events.
-         * Since OnChangeAjaxBehavior extends 
AjaxFormComponentUpdatingBehavior the Ajax request
-         * also updates the Wicket model for this form component on the server 
side.
-         */
-        textField.add(new OnChangeAjaxBehavior() {
-            private static final long serialVersionUID = 1L;
-            @Override
-            protected void onUpdate(final AjaxRequestTarget target) {
-                // triggers update of dependent args (action prompt)
-                ScalarPanelTextFieldWithTemporalPicker.this
-                    .getScalarModelChangeDispatcher().notifyUpdate(target);
-            }
-        });
+        val textField = new TextFieldWithDateTimePicker<T>(
+                        id, scalarModel.unwrapped(type), type, 
scalarModel.isRequired(),
+                        temporalDecomposition,
+                        temporalDecomposition.getEditingPattern());
 
-        return textField;
+        return installUpdateNotifier(textField);
     }
 
-    @Getter @Setter
-    private ZoneId zoneId;
-
-    @Getter @Setter
-    private ZoneOffset zoneOffset;
-
     @Override
     protected void onFormGroupCreated(final FormGroup formGroup) {
+        if(!scalarModel().isEditingMode()) return;
 
         // find the scalarValue container, which we want to add the additional 
fields to
         var container = WktComponents.findById(formGroup, 
"container-scalarValue", MarkupContainer.class)
@@ -104,14 +94,14 @@ extends ScalarPanelTextFieldWithValueSemantics<T>  {
         // create additional form fields that in combination with the main 
field make up the value
         switch (offsetCharacteristic()) {
         case OFFSET:
-            Wkt.dropDownChoiceAdd(container, "timeoffset",
-                    new 
PropertyModel<ZoneOffset>(ScalarPanelTextFieldWithTemporalPicker.this, 
"zoneOffset"),
+            Wkt.dropDownChoiceWithAjaxUpdateAdd(container, "timeoffset",
+                    new PropertyModel<ZoneOffset>(temporalDecomposition, 
"zoneOffset"),
                     temporalValueSemantics().getAvailableOffsets())
                 .setRequired(true);
             break;
         case ZONED:
-            Wkt.dropDownChoiceAdd(container, "timezone",
-                    new 
PropertyModel<ZoneId>(ScalarPanelTextFieldWithTemporalPicker.this, "zoneId"),
+            Wkt.dropDownChoiceWithAjaxUpdateAdd(container, "timezone",
+                    new PropertyModel<ZoneId>(temporalDecomposition, "zoneId"),
                     temporalValueSemantics().getAvailableZoneIds())
                 .setRequired(true);
             break;
@@ -144,16 +134,37 @@ extends ScalarPanelTextFieldWithValueSemantics<T>  {
 
     // -- HELPER
 
-    private TemporalValueSemantics<?> temporalValueSemantics() {
-        return Facets.valueTemporalSemantics(scalarModel().getElementType())
+    private TemporalValueSemantics<T> temporalValueSemantics() {
+        return 
_Casts.uncheckedCast(Facets.valueTemporalSemantics(scalarModel().getElementType())
                 .orElseThrow(()->_Exceptions.illegalState("no (temporal) value 
semantics found for %s",
-                        scalarModel().getElementType()));
+                        scalarModel().getElementType())));
+    }
+
+    //TODO[CAUSEWAY-3489] unused
+    private TemporalCharacteristic temporalCharacteristic() {
+        return temporalValueSemantics().getTemporalCharacteristic();
     }
 
     private OffsetCharacteristic offsetCharacteristic() {
-        return Facets.valueTemporalSemantics(scalarModel().getElementType())
-                .map(TemporalValueSemantics::getOffsetCharacteristic)
-                .orElse(OffsetCharacteristic.LOCAL);
+        return temporalValueSemantics().getOffsetCharacteristic();
+    }
+
+    private <X> TextField<X> installUpdateNotifier(final TextField<X> 
textField) {
+        /* [CAUSEWAY-3201]
+         * Adding OnChangeAjaxBehavior registers a JavaScript event listener 
on change events.
+         * Since OnChangeAjaxBehavior extends 
AjaxFormComponentUpdatingBehavior the Ajax request
+         * also updates the Wicket model for this form component on the server 
side.
+         */
+        textField.add(new OnChangeAjaxBehavior() {
+            private static final long serialVersionUID = 1L;
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                // triggers update of following args (action prompt)
+                ScalarPanelTextFieldWithTemporalPicker.this
+                    .getScalarModelChangeDispatcher().notifyUpdate(target);
+            }
+        });
+        return textField;
     }
 
 }
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/TemporalDecomposition.java
new file mode 100644
index 0000000000..6e4cb8bd7e
--- /dev/null
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TemporalDecomposition.java
@@ -0,0 +1,157 @@
+/*
+ *  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.viewer.wicket.ui.components.scalars.datepicker;
+
+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;
+
+import org.apache.wicket.util.convert.ConversionException;
+import org.apache.wicket.util.convert.IConverter;
+
+import org.apache.causeway.applib.value.semantics.TemporalValueSemantics;
+import 
org.apache.causeway.applib.value.semantics.TemporalValueSemantics.OffsetCharacteristic;
+import 
org.apache.causeway.applib.value.semantics.TemporalValueSemantics.TemporalCharacteristic;
+import org.apache.causeway.commons.internal.base._Temporals;
+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.viewer.wicket.model.models.ScalarModel;
+import 
org.apache.causeway.viewer.wicket.model.value.ConverterBasedOnValueSemantics;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Introduced to handle temporal values with zone or offset information
+ * based on existing widgets, that do not support zone or offset information.
+ */
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public class TemporalDecomposition<T extends Temporal> implements 
IConverter<T> {
+    private static final long serialVersionUID = 1L;
+
+    public static <T extends Temporal> TemporalDecomposition<T> create(final 
Class<T> type,
+            final ScalarModel scalarModel,
+            final TemporalValueSemantics<T> temporalValueSemantics,
+            final ConverterBasedOnValueSemantics<T> fullConverter) {
+
+        var baseEditingPattern = fullConverter.getEditingPattern();
+        //TODO[CAUSEWAY-3489] switch on offsetCharacteristic and 
temporalCharacteristic to do this properly
+        var editingPattern = scalarModel.isEditingMode()
+            ? "yyyy-MM-dd HH:mm:ss" // strip 'XXX' or 'VV';
+            : baseEditingPattern;
+
+        var tempDecomp = new TemporalDecomposition<>(type, 
temporalValueSemantics, fullConverter,
+                scalarModel.getViewOrEditMode(),
+                editingPattern);
+        tempDecomp.initFrom(scalarModel.proposedValue());
+        return tempDecomp;
+    }
+
+    private final @NonNull Class<T> type;
+    private final @NonNull TemporalValueSemantics<T> temporalValueSemantics;
+    private final @NonNull ConverterBasedOnValueSemantics<T> fullConverter;
+    private final @NonNull ViewOrEditMode viewOrEditMode;
+
+    @Getter
+    private final String editingPattern;
+
+    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();
+        }
+    }
+
+    @Override
+    public T convertToObject(final String noZoneValue, final Locale locale) 
throws ConversionException {
+        var fullTemporalString = noZoneValue + getZoneOrOffsetSuffix();
+        return fullConverter.convertToObject(fullTemporalString, locale);
+    }
+    @Override
+    public String convertToString(final T value, final Locale locale) {
+        return fullConverter.convertToString(value, locale);
+    }
+
+    // -- SPECIALIZATION
+
+    private TemporalCharacteristic temporalCharacteristic() {
+        return temporalValueSemantics.getTemporalCharacteristic();
+    }
+
+    private OffsetCharacteristic offsetCharacteristic() {
+        return temporalValueSemantics.getOffsetCharacteristic();
+    }
+
+    private String getZoneOrOffsetSuffix() {
+        switch (offsetCharacteristic()) {
+        case OFFSET:
+            return " " + _Temporals.formatZoneId(zoneOffset==null
+                    ? ZoneOffset.UTC
+                    : zoneOffset,
+                _Temporals.ISO_OFFSET_ONLY_FORMAT);
+        case ZONED:
+            return " " + _Temporals.formatZoneId(zoneId==null
+                    ? ZoneOffset.UTC
+                    : zoneId);
+        default:
+            return "";
+        }
+    }
+
+    // -- PROPERTIES
+
+    private ZoneId zoneId;
+    public ZoneId getZoneId() {
+        //TODO[CAUSEWAY-3489] remove debug line
+        System.err.printf("getZoneId %s%n", zoneId);
+        return zoneId;
+    }
+    public void setZoneId(final ZoneId zoneId) {
+        this.zoneId = zoneId;
+        //TODO[CAUSEWAY-3489] remove debug line
+        System.err.printf("setZoneId %s%n", zoneId);
+    }
+
+    private ZoneOffset zoneOffset;
+    public ZoneOffset getZoneOffset() {
+        //TODO[CAUSEWAY-3489] remove debug line
+        System.err.printf("getZoneOffset %s%n", zoneOffset);
+        return zoneOffset;
+    }
+    public void setZoneOffset(final ZoneOffset zoneOffset) {
+        this.zoneOffset = zoneOffset;
+       //TODO[CAUSEWAY-3489] remove debug line
+        System.err.printf("setZoneOffset %s%n", zoneOffset);
+    }
+
+}
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TextFieldWithDateTimePicker.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TextFieldWithDateTimePicker.java
index befe2820df..d8219b430b 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TextFieldWithDateTimePicker.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/scalars/datepicker/TextFieldWithDateTimePicker.java
@@ -27,12 +27,11 @@ import java.util.Optional;
 import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.model.IModel;
 import org.apache.wicket.util.convert.IConverter;
 
 import org.apache.causeway.applib.locale.UserLocale;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
-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.text.TextFieldWithConverter;
 
 import lombok.NonNull;
@@ -63,16 +62,17 @@ extends TextFieldWithConverter<T> {
 
     public TextFieldWithDateTimePicker(
             final @NonNull String id,
-            final @NonNull ScalarModel scalarModel,
+            final @NonNull IModel<T> model,
             final @NonNull Class<T> type,
-            final @NonNull IConverter<T> converter) {
-        super(id, scalarModel.unwrapped(type), type, Optional.of(converter));
+            final boolean isRequired,
+            final @NonNull IConverter<T> converter,
+            final @NonNull String editingPattern) {
+        super(id, model, type, Optional.of(converter));
         setOutputMarkupId(true);
 
         this.config = createDatePickerConfig(
-                scalarModel.getMetaModelContext(),
-                ((ConverterBasedOnValueSemantics<T>) 
converter).getEditingPattern(),
-                !scalarModel.isRequired());
+                editingPattern,
+                !isRequired);
 
         /* debug
                 new IConverter<T>() {
@@ -132,10 +132,10 @@ extends TextFieldWithConverter<T> {
     // -- HELPER
 
     private DateTimeConfig createDatePickerConfig(
-            final MetaModelContext mmc,
             final String temporalPattern,
             final boolean isInputNullable) {
         val config = new DateTimeConfig();
+        val mmc = MetaModelContext.instanceElseFail();
 
         config.useLocale(mmc.currentUserLocale()
                 .map(UserLocale::getLanguageLocale)
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/util/Wkt.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/util/Wkt.java
index ed7fb5de54..2bbf5569ee 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/util/Wkt.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/util/Wkt.java
@@ -56,6 +56,7 @@ import org.apache.wicket.markup.html.form.Button;
 import org.apache.wicket.markup.html.form.DropDownChoice;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.form.FormComponentUpdatingBehavior;
 import org.apache.wicket.markup.html.form.TextArea;
 import org.apache.wicket.markup.html.form.TextField;
 import org.apache.wicket.markup.html.form.upload.FileUpload;
@@ -619,12 +620,21 @@ public class Wkt {
         return new DropDownChoice<T>(id, model, choices);
     }
 
-    public <T extends Serializable> DropDownChoice<T> dropDownChoiceAdd(
+    public <T extends Serializable> DropDownChoice<T> 
dropDownChoiceWithAjaxUpdate(final String id,
+            final IModel<T> model,
+            final List<? extends T> choices) {
+        var component = dropDownChoice(id, model, choices);
+        component.setOutputMarkupId(true);
+        component.add(new FormComponentUpdatingBehavior());
+        return component;
+    }
+
+    public <T extends Serializable> DropDownChoice<T> 
dropDownChoiceWithAjaxUpdateAdd(
             final MarkupContainer container,
             final String id,
             final IModel<T> model,
             final List<? extends T> choices) {
-        return add(container, dropDownChoice(id, model, choices));
+        return add(container, dropDownChoiceWithAjaxUpdate(id, model, 
choices));
     }
 
     // -- FILE DOWNLOAD
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
index ade636d5ae..b0f1828f2c 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
@@ -19,6 +19,7 @@
 package org.apache.causeway.viewer.wicket.viewer.registries.components;
 
 import java.io.Serializable;
+import java.time.temporal.Temporal;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -37,6 +38,7 @@ import 
org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsResolver;
 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.functions._Predicates;
 import 
org.apache.causeway.core.metamodel.tabular.simple.CollectionContentsExporter;
@@ -279,6 +281,7 @@ public class ComponentFactoryRegistrarDefault implements 
ComponentFactoryRegistr
 
     // -- UTILTIY
 
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     public static <T extends Serializable> 
ComponentFactoryScalarTypeConstrainedAbstract
     createForValueSemantics(final ValueSemanticsProvider<T> valueSemantics) {
 
@@ -287,7 +290,7 @@ public class ComponentFactoryRegistrarDefault implements 
ComponentFactoryRegistr
         }
 
         if(valueSemantics.isTemporalType()) {
-            return new 
ScalarPanelFactoryForTemporalPicker<T>(valueSemantics.getCorrespondingClass());
+            return _Casts.uncheckedCast(new 
ScalarPanelFactoryForTemporalPicker(valueSemantics.getCorrespondingClass()));
         }
 
         if(valueSemantics.isCompositeType()) {
@@ -330,7 +333,7 @@ public class ComponentFactoryRegistrarDefault implements 
ComponentFactoryRegistr
         }
     }
 
-    public static class ScalarPanelFactoryForTemporalPicker<T extends 
Serializable>
+    public static class ScalarPanelFactoryForTemporalPicker<T extends 
Serializable & Temporal>
     extends ComponentFactoryScalarTypeConstrainedAbstract {
 
         private final Class<T> valueTypeClass;

Reply via email to