You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/12/01 07:41:09 UTC

[isis] branch master updated: ISIS-2906: add test to prove JsonRepresentation preserve Map ordering

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/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new e74d9f4  ISIS-2906: add test to prove JsonRepresentation preserve Map<K, V> ordering
e74d9f4 is described below

commit e74d9f4dfe34cbe3a66e8e946f00eb06dd3b69e6
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Dec 1 08:41:01 2021 +0100

    ISIS-2906: add test to prove JsonRepresentation preserve Map<K, V>
    ordering
    
    - also promotes IndexedFunction and indexedConsumer to official commons
    API
---
 .../isis/commons/functional/IndexedConsumer.java   |  61 +++++++++
 .../isis/commons/functional/IndexedFunction.java   |  61 +++++++++
 .../apache/isis/commons/internal/base/_Refs.java   |  11 +-
 .../apache/isis/commons/internal/base/_Text.java   |   6 +-
 .../commons/internal/debug/xray/XrayDataModel.java |  10 +-
 .../commons/internal/functions/_Functions.java     |  16 ---
 .../functions/_Functions_IndexedZeroBase.java      |  44 ------
 .../command/CommandExecutorServiceDefault.java     |   4 +-
 .../confmenu/ConfigurationViewServiceDefault.java  |   6 +-
 .../cli/adocfix/OrphanedIncludeStatementFixer.java |   2 +-
 .../isis/tooling/cli/projdoc/ProjectDocWriter.java |   2 +-
 .../isis/tooling/model4adoc/AsciiDocFactory.java   | 149 ++++++++++-----------
 .../applib/JsonRepresentationTest_putXxx.java      |  43 ++++--
 .../JsonRepresentationTest_streamMapEntries.java   |  14 +-
 .../components/tree/IsisToWicketTreeAdapter.java   |   4 +-
 .../ObjectAdapterMementoProviderAbstract.java      |  10 ++
 16 files changed, 272 insertions(+), 171 deletions(-)

diff --git a/commons/src/main/java/org/apache/isis/commons/functional/IndexedConsumer.java b/commons/src/main/java/org/apache/isis/commons/functional/IndexedConsumer.java
new file mode 100644
index 0000000..c632e6f
--- /dev/null
+++ b/commons/src/main/java/org/apache/isis/commons/functional/IndexedConsumer.java
@@ -0,0 +1,61 @@
+/*
+ *  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.isis.commons.functional;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+import org.apache.isis.commons.internal.base._Refs;
+
+import lombok.val;
+
+/**
+ * Similar to a {@link BiConsumer}, except that the first argument is an {@code int}.
+ *
+ * @since 2.x [@index}
+ */
+@FunctionalInterface
+public interface IndexedConsumer<T> {
+
+    // -- INTERFACE
+
+    void accept(int index, T t);
+
+    // -- UTILITY
+
+    /**
+     * Converts an {@link IndexedConsumer} into a {@link Consumer},
+     * having its index start at {@code offset},
+     * and incremented after each call.
+     */
+    static <T> Consumer<T> offset(final int offset, final IndexedConsumer<T> indexed){
+        val indexRef = _Refs.intRef(offset);
+        return t->indexed.accept(indexRef.getAndInc(), t);
+    }
+
+    /**
+     * Converts an {@link IndexedConsumer} into a {@link Consumer},
+     * having its index start at 0,
+     * and incremented after each call.
+     */
+    static <T> Consumer<T> zeroBased(final IndexedConsumer<T> indexed){
+        return offset(0, indexed);
+    }
+
+}
diff --git a/commons/src/main/java/org/apache/isis/commons/functional/IndexedFunction.java b/commons/src/main/java/org/apache/isis/commons/functional/IndexedFunction.java
new file mode 100644
index 0000000..99ccfa3
--- /dev/null
+++ b/commons/src/main/java/org/apache/isis/commons/functional/IndexedFunction.java
@@ -0,0 +1,61 @@
+/*
+ *  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.isis.commons.functional;
+
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import org.apache.isis.commons.internal.base._Refs;
+
+import lombok.val;
+
+/**
+ * Similar to a {@link BiFunction}, except that the first argument is an {@code int}.
+ *
+ * @since 2.x [@index}
+ */
+@FunctionalInterface
+public interface IndexedFunction<T, R> {
+
+    // -- INTERFACE
+
+    public R apply(int index, T t);
+
+    // -- UTILITY
+
+    /**
+     * Converts an {@link IndexedFunction} into a {@link Function},
+     * having its index start at {@code offset},
+     * and incremented after each call.
+     */
+    static <T, R> Function<T, R> offset(final int offset, final IndexedFunction<T, R> indexed){
+        val indexRef = _Refs.intRef(offset);
+        return t->indexed.apply(indexRef.getAndInc(), t);
+    }
+
+    /**
+     * Converts an {@link IndexedFunction} into a {@link Function},
+     * having its index start at 0,
+     * and incremented after each call.
+     */
+    static <T, R> Function<T, R> zeroBased(final IndexedFunction<T, R> indexed){
+        return offset(0, indexed);
+    }
+
+}
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/base/_Refs.java b/commons/src/main/java/org/apache/isis/commons/internal/base/_Refs.java
index 72c4ba1..86e8456 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/base/_Refs.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/base/_Refs.java
@@ -117,14 +117,21 @@ public final class _Refs {
             return value==other;
         }
 
-        public int inc() {
+        public int incAndGet() {
             return ++value;
         }
 
-        public int dec() {
+        public int decAndGet() {
             return --value;
         }
 
+        public int getAndInc() {
+            return value++;
+        }
+
+        public int gatAndDec() {
+            return value--;
+        }
     }
 
     /**
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/base/_Text.java b/commons/src/main/java/org/apache/isis/commons/internal/base/_Text.java
index e69a062..eb8616c 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/base/_Text.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/base/_Text.java
@@ -324,12 +324,12 @@ public final class _Text {
 
         if(na.size()<=nb.size()) {
             na.zip(nb, (left, right)->{
-                final int lineNr = lineNrRef.inc();
+                final int lineNr = lineNrRef.incAndGet();
                 _Assert.assertEquals(left, right, ()->String.format("first non matching lineNr %d", lineNr));
             });
         } else {
             nb.zip(na, (right, left)->{
-                final int lineNr = lineNrRef.inc();
+                final int lineNr = lineNrRef.incAndGet();
                 _Assert.assertEquals(left, right, ()->String.format("first non matching lineNr %d", lineNr));
             });
         }
@@ -396,7 +396,7 @@ public final class _Text {
             final int nextLen = partialSum.getValue() + tokenLen;
             if(nextLen <= maxChars) {
                 partialSum.update(x->nextLen);
-                partialCount.inc();
+                partialCount.incAndGet();
             } else {
 
                 constraintLines.add(
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayDataModel.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayDataModel.java
index 0573d55..be17ac7 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayDataModel.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayDataModel.java
@@ -64,7 +64,7 @@ public abstract class XrayDataModel extends HasIdAndLabel {
         private final String iconResource = "/xray/key-value.png";
 
         @Override
-        public void render(JScrollPane panel) {
+        public void render(final JScrollPane panel) {
             String[] columnNames = {"Key", "Value"};
             Object[][] tableData = new Object[data.size()][columnNames.length];
 
@@ -72,7 +72,7 @@ public abstract class XrayDataModel extends HasIdAndLabel {
 
             data.forEach((k, v)->{
                 val row = tableData[rowIndex.getValue()];
-                rowIndex.inc();
+                rowIndex.incAndGet();
                 row[0] = k;
                 row[1] = v;
             });
@@ -102,12 +102,12 @@ public abstract class XrayDataModel extends HasIdAndLabel {
         private final static Color BACKGROUND_COLOR = COLOR_SILVER;
         private final static Color BORDER_COLOR = Color.GRAY;
 
-        public Sequence(String label) {
+        public Sequence(final String label) {
             this(UUID.randomUUID().toString(), label);
         }
 
         @Override
-        public void render(JScrollPane panel) {
+        public void render(final JScrollPane panel) {
 
             val dim = data.layout((Graphics2D)panel.getGraphics());
 
@@ -115,7 +115,7 @@ public abstract class XrayDataModel extends HasIdAndLabel {
                 private static final long serialVersionUID = 1L;
 
                 @Override
-                public void paintComponent(Graphics _g) {
+                public void paintComponent(final Graphics _g) {
 
                     val g = (Graphics2D)_g;
 
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
index 62ed1c2..d45c23c 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
@@ -43,22 +43,6 @@ public final class _Functions {
         return t->{};
     }
 
-    // -- INDEX AWARE
-
-    @FunctionalInterface
-    public interface IndexedFunction<T, R> {
-        public R apply(int index, T t);
-    }
-
-    /**
-     * Converts an {@link IndexedFunction} into a {@link Function}, having its index start at 0,
-     * and incremented after each function call.
-     * @param indexed
-     */
-    public static <T, R> Function<T, R> indexedZeroBase(final IndexedFunction<T, R> indexed){
-        return new _Functions_IndexedZeroBase<T, R>(indexed);
-    }
-
     // -- CHECKED EXCEPTION ADAPTERS (FUNCTION)
 
     /**
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions_IndexedZeroBase.java b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions_IndexedZeroBase.java
deleted file mode 100644
index 6394c74..0000000
--- a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions_IndexedZeroBase.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *  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.isis.commons.internal.functions;
-
-import java.util.function.Function;
-
-import org.apache.isis.commons.internal.functions._Functions.IndexedFunction;
-
-/**
- * Package private mixin for _Functions. <br/>
- * Extending a Function to keep track of an index, incremented with each function call.
- */
-class _Functions_IndexedZeroBase<T, R> implements Function<T, R> {
-
-
-    private int index=0;
-    private final IndexedFunction<T, R> indexAwareFunction;
-
-    _Functions_IndexedZeroBase(IndexedFunction<T, R> indexAwareFunction) {
-        this.indexAwareFunction = indexAwareFunction;
-    }
-
-    @Override
-    public R apply(T t) {
-        return indexAwareFunction.apply(index++, t);
-    }
-
-}
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
index 8f4a0a3..55b1b2c 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
@@ -48,10 +48,10 @@ import org.apache.isis.applib.services.sudo.SudoService;
 import org.apache.isis.applib.services.xactn.TransactionService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.functional.IndexedFunction;
 import org.apache.isis.commons.functional.Result;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.commons.internal.functions._Functions;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.interactions.InteractionHead;
 import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
@@ -368,7 +368,7 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
         final Identifier actionIdentifier = valueMarshaller.actionIdentifier(actionDto);
 
         return streamParamDtosFrom(actionDto)
-                .map(_Functions.indexedZeroBase((i, paramDto)->
+                .map(IndexedFunction.zeroBased((i, paramDto)->
                         recoverValueFrom(actionIdentifier.withParameterIndex(i), paramDto)))
                 .collect(Can.toCan());
     }
diff --git a/core/webapp/src/main/java/org/apache/isis/core/webapp/confmenu/ConfigurationViewServiceDefault.java b/core/webapp/src/main/java/org/apache/isis/core/webapp/confmenu/ConfigurationViewServiceDefault.java
index 3993d82..438734d 100644
--- a/core/webapp/src/main/java/org/apache/isis/core/webapp/confmenu/ConfigurationViewServiceDefault.java
+++ b/core/webapp/src/main/java/org/apache/isis/core/webapp/confmenu/ConfigurationViewServiceDefault.java
@@ -182,7 +182,7 @@ implements
             val dsInfos = datasourceInfoService.getDataSourceInfos();
 
             dsInfos.forEach(dataSourceInfo->{
-                index.inc();
+                index.incAndGet();
                 add(String.format("Data Source (%d/%d)", index.getValue(), dsInfos.size()),
                         dataSourceInfo.getJdbcUrl(),
                         map);
@@ -197,12 +197,12 @@ implements
         return map;
     }
 
-    private static void add(String key, String value, Map<String, ConfigurationProperty> map) {
+    private static void add(final String key, String value, final Map<String, ConfigurationProperty> map) {
         value = ValueMaskingUtil.maskIfProtected(key, value);
         map.put(key, new ConfigurationProperty(key, value));
     }
 
-    private static void addSystemProperty(String key, Map<String, ConfigurationProperty> map) {
+    private static void addSystemProperty(final String key, final Map<String, ConfigurationProperty> map) {
         add(key, System.getProperty(key, "<empty>"), map);
     }
 
diff --git a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/adocfix/OrphanedIncludeStatementFixer.java b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/adocfix/OrphanedIncludeStatementFixer.java
index 84ca180..6a767cf 100644
--- a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/adocfix/OrphanedIncludeStatementFixer.java
+++ b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/adocfix/OrphanedIncludeStatementFixer.java
@@ -98,7 +98,7 @@ public final class OrphanedIncludeStatementFixer {
                     if(!includeLineShouldBe.equals(include.getMatchingLine())) {
                         log.warn("mismatch\n {}\n {}\n", includeLineShouldBe, include.getMatchingLine());
                         correctedIncludeStatement.setValue(expected);
-                        fixedCounter.inc();
+                        fixedCounter.incAndGet();
                     }
 
                 });
diff --git a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/projdoc/ProjectDocWriter.java b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/projdoc/ProjectDocWriter.java
index d642573..5b8d898 100644
--- a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/projdoc/ProjectDocWriter.java
+++ b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/projdoc/ProjectDocWriter.java
@@ -88,7 +88,7 @@ final class ProjectDocWriter {
                 })
                 .stream()
                 .peek(adocFile->log.debug("deleting file: {}", adocFile.getAbsolutePath()))
-                .peek(__->deleteCount.inc())
+                .peek(__->deleteCount.incAndGet())
                 .forEach(_Files::deleteFile);
 
                 // write document index
diff --git a/tooling/model4adoc/src/main/java/org/apache/isis/tooling/model4adoc/AsciiDocFactory.java b/tooling/model4adoc/src/main/java/org/apache/isis/tooling/model4adoc/AsciiDocFactory.java
index 49abe49..5259de1 100644
--- a/tooling/model4adoc/src/main/java/org/apache/isis/tooling/model4adoc/AsciiDocFactory.java
+++ b/tooling/model4adoc/src/main/java/org/apache/isis/tooling/model4adoc/AsciiDocFactory.java
@@ -21,8 +21,6 @@ package org.apache.isis.tooling.model4adoc;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import org.springframework.lang.Nullable;
-
 import org.asciidoctor.ast.Block;
 import org.asciidoctor.ast.Cell;
 import org.asciidoctor.ast.Column;
@@ -31,6 +29,7 @@ import org.asciidoctor.ast.ListItem;
 import org.asciidoctor.ast.Row;
 import org.asciidoctor.ast.StructuralNode;
 import org.asciidoctor.ast.Table;
+import org.springframework.lang.Nullable;
 
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Refs;
@@ -72,7 +71,7 @@ public class AsciiDocFactory {
     /**
      * syntactic sugar
      */
-    public static Document doc(Consumer<Document> documentBuilder) {
+    public static Document doc(final Consumer<Document> documentBuilder) {
         val doc = doc();
         documentBuilder.accept(doc);
         return doc;
@@ -81,7 +80,7 @@ public class AsciiDocFactory {
     /**
      * syntactic sugar
      */
-    public static String toString(Consumer<Document> documentBuilder) {
+    public static String toString(final Consumer<Document> documentBuilder) {
         val doc = doc();
         documentBuilder.accept(doc);
         return AsciiDocWriter.toString(doc);
@@ -89,7 +88,7 @@ public class AsciiDocFactory {
 
     // -- ATTRIBUTES
 
-    public static void attrNotice(Document node, String value) {
+    public static void attrNotice(final Document node, final String value) {
         node.setAttribute("notice", value, true);
     }
 
@@ -101,53 +100,53 @@ public class AsciiDocFactory {
     //CAUTION
     //WARNING
 
-    public static Block note(StructuralNode parent) {
+    public static Block note(final StructuralNode parent) {
         return admonition("Note", parent, null);
     }
 
-    public static Block note(StructuralNode parent, String source) {
+    public static Block note(final StructuralNode parent, final String source) {
         return admonition("Note", parent, source);
     }
 
-    public static Block tip(StructuralNode parent) {
+    public static Block tip(final StructuralNode parent) {
         return admonition("Tip", parent, null);
     }
 
-    public static Block tip(StructuralNode parent, String source) {
+    public static Block tip(final StructuralNode parent, final String source) {
         return admonition("Tip", parent, source);
     }
 
-    public static Block important(StructuralNode parent) {
+    public static Block important(final StructuralNode parent) {
         return admonition("Important", parent, null);
     }
 
-    public static Block important(StructuralNode parent, String source) {
+    public static Block important(final StructuralNode parent, final String source) {
         return admonition("Important", parent, source);
     }
 
-    public static Block caution(StructuralNode parent) {
+    public static Block caution(final StructuralNode parent) {
         return admonition("Caution", parent, null);
     }
 
-    public static Block caution(StructuralNode parent, String source) {
+    public static Block caution(final StructuralNode parent, final String source) {
         return admonition("Caution", parent, source);
     }
 
-    public static Block warning(StructuralNode parent) {
+    public static Block warning(final StructuralNode parent) {
         return admonition("Warning", parent, null);
     }
 
-    public static Block warning(StructuralNode parent, String source) {
+    public static Block warning(final StructuralNode parent, final String source) {
         return admonition("Warning", parent, source);
     }
 
     // -- BLOCK
 
-    public static Block block(StructuralNode parent) {
+    public static Block block(final StructuralNode parent) {
         return block(parent, null);
     }
 
-    public static Block block(StructuralNode parent, String source) {
+    public static Block block(final StructuralNode parent, final String source) {
         val block = new SimpleBlock();
         block.setSource(source);
         block.setLevel(parent.getLevel());
@@ -156,19 +155,19 @@ public class AsciiDocFactory {
         return block;
     }
 
-    public static Block openBlock(ListItem listItem) {
+    public static Block openBlock(final ListItem listItem) {
         val openBlock = block(listItem);
         openBlock.setStyle("open");
         return openBlock;
     }
 
-    public static Block listingBlock(StructuralNode parent, @NonNull String source) {
+    public static Block listingBlock(final StructuralNode parent, @NonNull final String source) {
         val listingBlock = block(parent, source);
         listingBlock.setStyle("listing");
         return listingBlock;
     }
 
-    public static Block sourceBlock(StructuralNode parent, @Nullable String language, @NonNull String source) {
+    public static Block sourceBlock(final StructuralNode parent, @Nullable final String language, @NonNull final String source) {
         val sourceBlock = block(parent, source);
         sourceBlock.setStyle("source");
         if(_Strings.isNotEmpty(language)) {
@@ -178,10 +177,10 @@ public class AsciiDocFactory {
     }
 
     public static Block diagramBlock(
-            StructuralNode parent,
-            @NonNull String diagramType,
-            @NonNull Can<String> diagramOptions,
-            @NonNull String source) {
+            final StructuralNode parent,
+            @NonNull final String diagramType,
+            @NonNull final Can<String> diagramOptions,
+            @NonNull final String source) {
 
         val diagramBlock = block(parent, source);
 
@@ -194,7 +193,7 @@ public class AsciiDocFactory {
         // add options
         attributes.forEach(opt->{
             diagramBlock.setAttribute(""+attributeIndex.getValue(), opt, true);
-            attributeIndex.inc();
+            attributeIndex.incAndGet();
         });
 
         return diagramBlock;
@@ -202,19 +201,19 @@ public class AsciiDocFactory {
 
     // -- CALLOUTS
 
-    public static org.asciidoctor.ast.List callouts(StructuralNode parent) {
+    public static org.asciidoctor.ast.List callouts(final StructuralNode parent) {
         val calloutList = list(parent);
         calloutList.setStyle("arabic");
         return calloutList;
     }
 
-    public static ListItem callout(org.asciidoctor.ast.List parent, @NonNull String source) {
+    public static ListItem callout(final org.asciidoctor.ast.List parent, @NonNull final String source) {
         return listItem(parent, source);
     }
 
     // -- COLLAPSIBLE
 
-    public static Block collapsibleBlock(StructuralNode parent, @NonNull String source) {
+    public static Block collapsibleBlock(final StructuralNode parent, @NonNull final String source) {
         val collapsibleBlock = block(parent, source);
         collapsibleBlock.setStyle("example");
         collapsibleBlock.setAttribute("collapsible-option", "1", true);
@@ -223,39 +222,39 @@ public class AsciiDocFactory {
 
     // -- TABLE
 
-    public static Table table(StructuralNode parent) {
+    public static Table table(final StructuralNode parent) {
         val table = new SimpleTable();
         parent.getBlocks().add(table);
         table.setParent(parent);
         return table;
     }
 
-    public static Column col(Table table) {
+    public static Column col(final Table table) {
         val column = new SimpleColumn();
         table.getColumns().add(column);
         column.setParent(table);
         return column;
     }
 
-    public static Row row(Table table) {
+    public static Row row(final Table table) {
         val row = new SimpleRow();
         table.getBody().add(row);
         return row;
     }
 
-    public static Row headRow(Table table) {
+    public static Row headRow(final Table table) {
         val row = new SimpleRow();
         table.getHeader().add(row);
         return row;
     }
 
-    public static Row footRow(Table table) {
+    public static Row footRow(final Table table) {
         val row = new SimpleRow();
         table.getFooter().add(row);
         return row;
     }
 
-    public static Cell cell(Row row, Column column, String source) {
+    public static Cell cell(final Row row, final Column column, final String source) {
         val cell = new SimpleCell();
         row.getCells().add(cell);
         cell.setParent(column);
@@ -263,31 +262,31 @@ public class AsciiDocFactory {
         return cell;
     }
 
-    public static Cell cell(Table table, Row row, String source) {
+    public static Cell cell(final Table table, final Row row, final String source) {
         val colIndex = row.getCells().size();
         val column = getOrCreateColumn(table, colIndex);
         return cell(row, column, source);
     }
 
-    public static Cell cell(Table table, int rowIndex, int colIndex, String source) {
+    public static Cell cell(final Table table, final int rowIndex, final int colIndex, final String source) {
         val row = getOrCreateRow(table, rowIndex);
         val col = getOrCreateColumn(table, colIndex);
         return cell(row, col, source);
     }
 
-    public static Cell headCell(Table table, int rowIndex, int colIndex, String source) {
+    public static Cell headCell(final Table table, final int rowIndex, final int colIndex, final String source) {
         val row = getOrCreateHeadRow(table, rowIndex);
         val col = getOrCreateColumn(table, colIndex);
         return cell(row, col, source);
     }
 
-    public static Cell footCell(Table table, int rowIndex, int colIndex, String source) {
+    public static Cell footCell(final Table table, final int rowIndex, final int colIndex, final String source) {
         val row = getOrCreateFootRow(table, rowIndex);
         val col = getOrCreateColumn(table, colIndex);
         return cell(row, col, source);
     }
 
-    public static org.asciidoctor.ast.List list(StructuralNode parent) {
+    public static org.asciidoctor.ast.List list(final StructuralNode parent) {
         val list = new SimpleList();
         list.setLevel(parent.getLevel()+1);
         parent.getBlocks().add(list);
@@ -295,11 +294,11 @@ public class AsciiDocFactory {
         return list;
     }
 
-    public static ListItem listItem(org.asciidoctor.ast.List parent) {
+    public static ListItem listItem(final org.asciidoctor.ast.List parent) {
         return listItem(parent, null);
     }
 
-    public static ListItem listItem(org.asciidoctor.ast.List parent, String source) {
+    public static ListItem listItem(final org.asciidoctor.ast.List parent, final String source) {
         val listItem = new SimpleListItem();
         listItem.setLevel(parent.getLevel());
         parent.getItems().add(listItem);
@@ -311,10 +310,10 @@ public class AsciiDocFactory {
     public static class SourceFactory {
 
         public static Block sourceBlock(
-                @NonNull Document doc,
-                @NonNull String languageAndOptions,
-                @NonNull String source,
-                @Nullable String title) {
+                @NonNull final Document doc,
+                @NonNull final String languageAndOptions,
+                @NonNull final String source,
+                @Nullable final String title) {
 
             val sourceBlock = AsciiDocFactory.sourceBlock(doc,
                     languageAndOptions,
@@ -343,30 +342,30 @@ public class AsciiDocFactory {
 //        }
 //        ----
         public static Block java(
-                @NonNull Document doc,
-                @NonNull String javaSource,
-                @Nullable String title) {
+                @NonNull final Document doc,
+                @NonNull final String javaSource,
+                @Nullable final String title) {
             return sourceBlock(doc, "java", javaSource, title);
         }
 
         public static Block json(
-                @NonNull Document doc,
-                @NonNull String jsonSource,
-                @Nullable String title) {
+                @NonNull final Document doc,
+                @NonNull final String jsonSource,
+                @Nullable final String title) {
             return sourceBlock(doc, "json", jsonSource, title);
         }
 
         public static Block xml(
-                @NonNull Document doc,
-                @NonNull String xmlSource,
-                @Nullable String title) {
+                @NonNull final Document doc,
+                @NonNull final String xmlSource,
+                @Nullable final String title) {
             return sourceBlock(doc, "xml", xmlSource, title);
         }
 
         public static Block yaml(
-                @NonNull Document doc,
-                @NonNull String yamlSource,
-                @Nullable String title) {
+                @NonNull final Document doc,
+                @NonNull final String yamlSource,
+                @Nullable final String title) {
             return sourceBlock(doc, "yaml", yamlSource, title);
         }
 
@@ -375,11 +374,11 @@ public class AsciiDocFactory {
     public static class DiagramFactory {
 
         public static Block diagramBlock(
-                @NonNull Document doc,
-                @NonNull String diagramType,
-                @NonNull Can<String> diagramOptions,
-                @NonNull String source,
-                @Nullable String title) {
+                @NonNull final Document doc,
+                @NonNull final String diagramType,
+                @NonNull final Can<String> diagramOptions,
+                @NonNull final String source,
+                @Nullable final String title) {
 
             val sourceBlock = AsciiDocFactory.diagramBlock(doc,
                     diagramType,
@@ -401,18 +400,18 @@ public class AsciiDocFactory {
 //      @enduml
 //      ----
         public static Block plantumlPng(
-                @NonNull Document doc,
-                @NonNull String plantumlSource,
-                @NonNull String diagramKey,
-                @Nullable String title) {
+                @NonNull final Document doc,
+                @NonNull final String plantumlSource,
+                @NonNull final String diagramKey,
+                @Nullable final String title) {
             return diagramBlock(doc, "plantuml", Can.of(diagramKey, "png"), plantumlSource, title);
         }
 
         public static Block plantumlSvg(
-                @NonNull Document doc,
-                @NonNull String plantumlSource,
-                @NonNull String diagramKey,
-                @Nullable String title) {
+                @NonNull final Document doc,
+                @NonNull final String plantumlSource,
+                @NonNull final String diagramKey,
+                @Nullable final String title) {
             return diagramBlock(doc, "plantuml", Can.of(diagramKey, "svg"), plantumlSource, title);
         }
 
@@ -421,7 +420,7 @@ public class AsciiDocFactory {
 
     // -- HELPER
 
-    private static Column getOrCreateColumn(Table table, int colIndex) {
+    private static Column getOrCreateColumn(final Table table, final int colIndex) {
         int maxIndexAvailable = table.getColumns().size() - 1;
         int colsToBeCreated = colIndex - maxIndexAvailable;
         for(int i=0; i<colsToBeCreated; ++i) {
@@ -430,7 +429,7 @@ public class AsciiDocFactory {
         return table.getColumns().get(colIndex);
     }
 
-    private static Row getOrCreateRow(Table table, int rowIndex) {
+    private static Row getOrCreateRow(final Table table, final int rowIndex) {
         int maxIndexAvailable = table.getBody().size() - 1;
         int rowsToBeCreated = rowIndex - maxIndexAvailable;
         for(int i=0; i<rowsToBeCreated; ++i) {
@@ -439,7 +438,7 @@ public class AsciiDocFactory {
         return table.getBody().get(rowIndex);
     }
 
-    private static Row getOrCreateHeadRow(Table table, int rowIndex) {
+    private static Row getOrCreateHeadRow(final Table table, final int rowIndex) {
         int maxIndexAvailable = table.getHeader().size() - 1;
         int rowsToBeCreated = rowIndex - maxIndexAvailable;
         for(int i=0; i<rowsToBeCreated; ++i) {
@@ -448,7 +447,7 @@ public class AsciiDocFactory {
         return table.getHeader().get(rowIndex);
     }
 
-    private static Row getOrCreateFootRow(Table table, int rowIndex) {
+    private static Row getOrCreateFootRow(final Table table, final int rowIndex) {
         int maxIndexAvailable = table.getFooter().size() - 1;
         int rowsToBeCreated = rowIndex - maxIndexAvailable;
         for(int i=0; i<rowsToBeCreated; ++i) {
@@ -457,7 +456,7 @@ public class AsciiDocFactory {
         return table.getFooter().get(rowIndex);
     }
 
-    private static Block admonition(String label, StructuralNode parent, String source) {
+    private static Block admonition(final String label, final StructuralNode parent, final String source) {
         val admonition = block(parent, source);
         admonition.setStyle(label.toUpperCase());
         admonition.setAttribute("textlabel", label, true);
diff --git a/viewers/restfulobjects/applib/src/test/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentationTest_putXxx.java b/viewers/restfulobjects/applib/src/test/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentationTest_putXxx.java
index 1e76b1d..b50b2ce 100644
--- a/viewers/restfulobjects/applib/src/test/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentationTest_putXxx.java
+++ b/viewers/restfulobjects/applib/src/test/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentationTest_putXxx.java
@@ -19,41 +19,60 @@
 package org.apache.isis.viewer.restfulobjects.applib;
 
 import java.io.IOException;
+import java.util.Random;
 
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
-public class JsonRepresentationTest_putXxx {
+import org.apache.isis.commons.functional.IndexedConsumer;
+
+class JsonRepresentationTest_putXxx {
 
     private JsonRepresentation jsonRepresentation;
 
-    @Before
-    public void setUp() throws Exception {
+    @BeforeEach
+    void setUp() throws Exception {
         jsonRepresentation = JsonRepresentation.newMap();
     }
 
     @Test
-    public void putInt() throws IOException {
+    void putInt() throws IOException {
         jsonRepresentation.mapPut("a", 123);
         assertThat(jsonRepresentation.getInt("a"), is(123));
     }
 
     @Test
-    public void putInt_multipart() throws IOException {
+    void putInt_multipart() throws IOException {
         jsonRepresentation.mapPut("a.b", 456);
         assertThat(jsonRepresentation.getInt("a.b"), is(456));
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void putInt_pathBlockedByValue() throws IOException {
-        // given
+    @Test
+    void putInt_pathBlockedByValue() throws IOException {
         jsonRepresentation.mapPut("a", 123);
+        assertThrows(IllegalArgumentException.class, ()->
+            jsonRepresentation.mapPut("a.b", 456));
+    }
 
-        // when
-        jsonRepresentation.mapPut("a.b", 456);
+    @Test
+    void preserveMapOrder() throws IOException {
+        final int[] values = new Random().ints(128, -4096, 4096).toArray();
+        {
+            int index = 0;
+            for(int value : values) {
+                jsonRepresentation.mapPut("i" + index, value);
+                ++index;
+            }
+        }
+
+        jsonRepresentation.streamMapEntries()
+        .forEach(IndexedConsumer.zeroBased((index, entry)->{
+            assertThat(entry.getKey(), is("i" + index));
+        }));
     }
 
 }
diff --git a/viewers/restfulobjects/applib/src/test/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentationTest_streamMapEntries.java b/viewers/restfulobjects/applib/src/test/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentationTest_streamMapEntries.java
index e72340e..21dc4fd 100644
--- a/viewers/restfulobjects/applib/src/test/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentationTest_streamMapEntries.java
+++ b/viewers/restfulobjects/applib/src/test/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentationTest_streamMapEntries.java
@@ -22,7 +22,7 @@ import java.io.IOException;
 import java.util.Iterator;
 import java.util.Map.Entry;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
@@ -31,20 +31,24 @@ import static org.hamcrest.MatcherAssert.assertThat;
 
 import static org.apache.isis.viewer.restfulobjects.applib.JsonFixture.readJson;
 
-public class JsonRepresentationTest_streamMapEntries {
+import lombok.val;
+
+class JsonRepresentationTest_streamMapEntries {
 
     private JsonRepresentation jsonRepresentation;
 
     @Test
-    public void forJsonRepresentation() throws IOException {
+    void forJsonRepresentation() throws IOException {
         jsonRepresentation = new JsonRepresentation(readJson("map.json"));
-        final Iterator<Entry<String, JsonRepresentation>> mapIterator = 
+        final Iterator<Entry<String, JsonRepresentation>> mapIterator =
                 jsonRepresentation.streamMapEntries()
                 .iterator();
 
         for (int i = 0; i < jsonRepresentation.asJsonNode().size(); i++) {
             assertThat(mapIterator.hasNext(), is(true));
-            assertThat(mapIterator.next().getKey(), is(not(nullValue())));
+
+            val next = mapIterator.next();
+            assertThat(next.getKey(), is(not(nullValue())));
         }
         assertThat(mapIterator.hasNext(), is(false));
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
index c0910d7..de4c749 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
@@ -45,8 +45,8 @@ import org.apache.isis.applib.graph.tree.TreeNode;
 import org.apache.isis.applib.graph.tree.TreePath;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.factory.FactoryService;
+import org.apache.isis.commons.functional.IndexedFunction;
 import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.functions._Functions;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.runtime.context.IsisAppCommonContext;
@@ -312,7 +312,7 @@ class IsisToWicketTreeAdapter {
         }
 
         private Function<Object, TreeModel> newPojoToTreeModelMapper(final TreeModel parent) {
-            return _Functions.indexedZeroBase((indexWithinSiblings, pojo)->
+            return IndexedFunction.zeroBased((indexWithinSiblings, pojo)->
             wrap(pojo, parent.getTreePath().append(indexWithinSiblings)));
         }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
index b5a289d..aa3be8d 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
@@ -28,8 +28,10 @@ import org.wicketstuff.select2.ChoiceProvider;
 
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.objectmanager.memento.ObjectMemento;
 import org.apache.isis.core.metamodel.objectmanager.memento.ObjectMementoForEmpty;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
@@ -67,6 +69,14 @@ extends ChoiceProvider<ObjectMemento> {
         if(ManagedObjects.isNullOrUnspecifiedOrEmpty(choice)) {
             return "Internal error: broken memento " + choiceMemento;
         }
+        val state = ManagedObjects.EntityUtil.getEntityState(choice);
+
+        if(state.isPersistable()) {
+            _Assert.assertTrue(state.isAttached());
+            val entityFacet = choice.getSpecification().getFacet(EntityFacet.class);
+            entityFacet.persist(choice.getSpecification(), choice.getPojo());
+        }
+
         return choice.titleString();
     }