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 d0da9ee945 CAUSEWAY-3586: adds nullable memento support for the 
ViewModel programming model
d0da9ee945 is described below

commit d0da9ee945b61947ae1c0af4a523963fee7fd17a
Author: Andi Huber <[email protected]>
AuthorDate: Thu Sep 28 12:22:57 2023 +0200

    CAUSEWAY-3586: adds nullable memento support for the ViewModel
    programming model
---
 .../ViewModelFacetForViewModelInterface.java       | 44 +++++++++++++++++++++-
 1 file changed, 42 insertions(+), 2 deletions(-)

diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacetForViewModelInterface.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacetForViewModelInterface.java
index e2dd9ce65f..264a5f3a50 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacetForViewModelInterface.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacetForViewModelInterface.java
@@ -19,6 +19,8 @@
 package org.apache.causeway.core.metamodel.facets.object.viewmodel;
 
 import java.lang.reflect.Constructor;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -39,9 +41,12 @@ import 
org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import 
org.apache.causeway.core.metamodel.specloader.validator.ValidationFailure;
 
+import lombok.Getter;
 import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
 import lombok.SneakyThrows;
 import lombok.val;
+import lombok.experimental.Accessors;
 
 /**
  * Corresponds to {@link ViewModel} interface.
@@ -135,11 +140,46 @@ extends ViewModelFacetAbstract {
     @Override
     public String serialize(final ManagedObject viewModel) {
         final ViewModel viewModelPojo = (ViewModel) viewModel.getPojo();
-        return UrlUtils.urlEncodeUtf8(viewModelPojo.viewModelMemento());
+        return SpecialMemento.encode(viewModelPojo.viewModelMemento());
     }
 
     // -- HELPER
 
+    /**
+     * In support of (stateless) {@link ViewModel}s that don't use a memento,
+     * or if an empty memento String is actually considered a valid use-case.
+     * (e.g. a Viewmodel that simply holds a String value for a search say)
+     * @apiNote introduced so we can create valid bookmarks,
+     *      that must have a non-empty identifier part
+     * @implNote the pipe character '|' is regarded unsafe,
+     *      hence gets processed by {@link URLEncoder} and {@link URLDecoder},
+     *      which makes it safe for us to use with special meaning
+     */
+    @Getter @Accessors(fluent=true)
+    @RequiredArgsConstructor
+    enum SpecialMemento {
+        EMPTY("||"),
+        NULL("|");
+        static String encode(final @Nullable String memento) {
+            return memento==null
+                    ? NULL.representationInUrl()
+                    : memento.isEmpty()
+                            ? EMPTY.representationInUrl()
+                            : UrlUtils.urlEncodeUtf8(memento);
+        }
+        static String decode(final @Nullable String memento) {
+            return NULL.matches(memento)
+                    ? null
+                    : EMPTY.matches(memento)
+                            ? ""
+                            : UrlUtils.urlDecodeUtf8(memento);
+        }
+        final String representationInUrl;
+        boolean matches(final String other) {
+            return representationInUrl.equals(other);
+        }
+    }
+
     @SneakyThrows
     private Object deserialize(
             @NonNull final ObjectSpecification viewmodelSpec,
@@ -148,7 +188,7 @@ extends ViewModelFacetAbstract {
         _Assert.assertNotNull(constructorAnyArgs, ()->"framework bug: required 
non-null, "
                 + "this can only happen, if we try to deserialize an abstract 
type");
 
-        val memento = UrlUtils.urlDecodeUtf8(mementoEncoded);
+        val memento = SpecialMemento.decode(mementoEncoded);
         val resolvedArgs = resolveArgsForConstructor(constructorAnyArgs, 
getServiceRegistry(), memento);
         val viewmodelPojo = 
constructorAnyArgs.constructor().newInstance(resolvedArgs);
         return viewmodelPojo;

Reply via email to