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 0db7e238dc ISIS-3127: [RO] render composite types as Map
0db7e238dc is described below

commit 0db7e238dc4b0af9ffafa3c7646e813897136874
Author: andi-huber <[email protected]>
AuthorDate: Fri Aug 19 15:56:16 2022 +0200

    ISIS-3127: [RO] render composite types as Map
---
 .../isis/testdomain/rest/JsonValueEncoderTest.java | 42 ++++++++++++++--
 .../restfulobjects/applib/JsonRepresentation.java  | 27 ++++++++---
 .../domainobjects/JsonValueConverters.java         | 15 ++++--
 .../rendering/domainobjects/JsonValueEncoder.java  | 56 ++++++++++++++++------
 .../domainobjects/ObjectPropertyReprRenderer.java  |  8 ++--
 5 files changed, 114 insertions(+), 34 deletions(-)

diff --git 
a/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/JsonValueEncoderTest.java
 
b/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/JsonValueEncoderTest.java
index 0970b002c2..da1a0ea318 100644
--- 
a/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/JsonValueEncoderTest.java
+++ 
b/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/JsonValueEncoderTest.java
@@ -30,9 +30,9 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
 
 import org.apache.isis.applib.value.Blob;
+import org.apache.isis.applib.value.Clob;
 import org.apache.isis.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.isis.commons.internal.base._Temporals;
-import org.apache.isis.commons.internal.functions._Functions;
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.testdomain.conf.Configuration_headless;
@@ -368,10 +368,41 @@ class JsonValueEncoderTest {
     @Test
     void whenBlob() {
         val value = Blob.of("a Blob", CommonMimeType.BIN, new byte[] {1, 2, 
3});
-        val representation = representationFor(value, 
_Functions.noopConsumer());
+        val representation = representationFor(value, osObj->assertEquals(
+                    JsonRepresentation.newMap("name", "a Blob.bin",
+                            "mimeType", "application/octet-stream",
+                            "bytes", "AQID").toString(),
+                    osObj.toString()));
 
-        System.err.printf("representation %s%n", representation);
-        //TODO convert repr. to map like
+        assertThat(representation.getString("extensions.x-isis-format"), 
is("blob"));
+    }
+
+    @Test
+    void whenClob() {
+        val value = Clob.of("a Clob", CommonMimeType.TXT, "abc");
+        val representation = representationFor(value, osObj->assertEquals(
+                    JsonRepresentation.newMap("name", "a Clob.txt",
+                            "mimeType", "text/plain",
+                            "chars", "abc").toString(),
+                    osObj.toString()));
+
+        assertThat(representation.getString("extensions.x-isis-format"), 
is("clob"));
+    }
+
+    static enum SampleEnum {
+        HALLO,
+        WORLD
+    }
+
+    @Test
+    void whenEnum() {
+        val value = SampleEnum.HALLO;
+        val representation = representationFor(value, osObj->assertEquals(
+                    JsonRepresentation.newMap("enumType", 
SampleEnum.class.getName(),
+                            "enumName", value.name()).toString(),
+                    osObj.toString()));
+
+        assertThat(representation.getString("extensions.x-isis-format"), 
is("enum"));
     }
 
     private JsonRepresentation representationFor(final Object value) {
@@ -395,6 +426,9 @@ class JsonValueEncoderTest {
 
         val representation = JsonRepresentation.newMap();
         jsonValueEncoder.appendValueAndFormat(valueAdapter, representation, 
context);
+
+        //debug
+        //System.err.printf("value %s-> %s %n", 
valueAdapter.getSpecification().getCorrespondingClass(), representation);
         return representation;
     }
 
diff --git 
a/viewers/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentation.java
 
b/viewers/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentation.java
index 3d619d7ca4..62e1d5b1ab 100644
--- 
a/viewers/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentation.java
+++ 
b/viewers/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentation.java
@@ -48,12 +48,17 @@ import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
 import org.springframework.lang.Nullable;
 
+import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.commons.internal.resources._Json;
 import org.apache.isis.viewer.restfulobjects.applib.util.JsonNodeUtils;
 import org.apache.isis.viewer.restfulobjects.applib.util.PathNode;
 import org.apache.isis.viewer.restfulobjects.applib.util.UrlEncodingUtils;
 
+import lombok.val;
+
 /**
  * A wrapper around {@link JsonNode} that provides some additional helper
  * methods.
@@ -120,6 +125,19 @@ public class JsonRepresentation {
         return transformer;
     }
 
+    public static JsonRepresentation jsonAsMap(final @Nullable String 
keyValuePairsAsJson) {
+        val repr = JsonRepresentation.newMap();
+        if(_Strings.isNotEmpty(keyValuePairsAsJson)) {
+            final Map<Object, Object> keyValuePairs = _Casts.uncheckedCast(
+                    _Json.readJson(Map.class, 
keyValuePairsAsJson).getValue().orElseThrow());
+
+            keyValuePairs.forEach((key, value)->{
+                repr.mapPutString(""+key, ""+value);
+            });
+        }
+        return repr;
+    }
+
     public static JsonRepresentation newMap(final String... keyValuePairs) {
         final JsonRepresentation repr = new JsonRepresentation(new 
ObjectNode(JsonNodeFactory.instance));
         String key = null;
@@ -1759,9 +1777,7 @@ public class JsonRepresentation {
     }
 
 
-    // ///////////////////////////////////////////////////////////////////////
-    // equals and hashcode
-    // ///////////////////////////////////////////////////////////////////////
+    // -- OBJECT CONTRACT (NO SEMANTIC EQUALITY) ... use toString() to check 
for semantic equality
 
     @Override
     public int hashCode() {
@@ -1782,15 +1798,12 @@ public class JsonRepresentation {
         JsonRepresentation other = (JsonRepresentation) obj;
         if (jsonNode == null) {
             if (other.jsonNode != null)
-                return false;
+            return false;
         } else if (!jsonNode.equals(other.jsonNode))
             return false;
         return true;
     }
 
-    // ///////////////////////////////////////////////////////////////////////
-    // toString
-    // ///////////////////////////////////////////////////////////////////////
 
     @Override
     public String toString() {
diff --git 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueConverters.java
 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueConverters.java
index 3def057737..fa6b7d59dd 100644
--- 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueConverters.java
+++ 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueConverters.java
@@ -30,7 +30,6 @@ import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
 
-import org.apache.isis.applib.value.semantics.ValueSemanticsProvider;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
@@ -40,10 +39,8 @@ import lombok.val;
 
 /**
  * Has support for JSON primitives.
- * @deprecated should be covered 100% per {@link ValueSemanticsProvider}
  */
-@Deprecated
-public final class JsonValueConverters {
+final class JsonValueConverters {
 
     @RequiredArgsConstructor
     public static enum DefaultFormat {
@@ -56,14 +53,24 @@ public final class JsonValueConverters {
         FLOAT(Float.class, "decimal", "float"),
         DOUBLE(Double.class, "decimal", "double"),
         CHAR(Character.class, null, "char"),
+
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         BIGINTEGER(BigInteger.class, "big-integer(18)", "javamathbiginteger"),
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         BIGDECIMAL(BigDecimal.class, "big-decimal", "javamathbigdecimal"),
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         JODALOCALDATE(LocalDate.class, "date", "jodalocaldate"),
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         JODALOCALDATETIME(LocalDateTime.class, "date-time", 
"jodalocaldatetime"),
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         JODADATETIME(DateTime.class, "date-time", "jodadatetime"),
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         JAVAUTILDATE(java.util.Date.class, "date-time", "javautildate"),
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         JAVASQLDATE(java.sql.Date.class, "date", "javasqldate"),
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         JAVASQLTIME(java.sql.Time.class, "time", "javasqltime"),
+        @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
         JAVASQLTIMESTAMP(java.sql.Timestamp.class, "utc-millisec", 
"javasqltimestamp"),
 
         ;
diff --git 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
index 22f0513b76..f49c5ee5b3 100644
--- 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
+++ 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
@@ -20,6 +20,7 @@ package 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects;
 
 import java.util.Map;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.Priority;
@@ -44,6 +45,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.metamodel.util.Facets;
+import org.apache.isis.schema.common.v2.ValueType;
 import 
org.apache.isis.viewer.restfulobjects.applib.IsisModuleViewerRestfulObjectsApplib;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import 
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueConverter.Context;
@@ -122,7 +124,7 @@ public class JsonValueEncoder {
         throw new IllegalArgumentException("Could not parse value '" + 
argValueRepr.asString() + "' as a " + objectSpec.getFullIdentifier());
     }
 
-    public Object appendValueAndFormat(
+    public void appendValueAndFormat(
             final ManagedObject valueAdapter,
             final JsonRepresentation repr,
             final Context context) {
@@ -131,17 +133,43 @@ public class JsonValueEncoder {
         val valueClass = valueSpec.getCorrespondingClass();
         val jsonValueConverter = converterByClass.get(valueClass);
         if(jsonValueConverter != null) {
-            return jsonValueConverter.appendValueAndFormat(valueAdapter, 
context, repr);
+            jsonValueConverter.appendValueAndFormat(valueAdapter, context, 
repr);
+            return;
         } else {
             final Optional<ValueDecomposition> valueDecompositionIfAny = 
decompose(valueAdapter);
-
             if(valueDecompositionIfAny.isPresent()) {
-                val value = valueDecompositionIfAny.get().toJson();
-                repr.mapPutString("value", value);
-                appendFormats(repr, "string", "string", 
context.isSuppressExtensions());
-                return value;
+                val valueDecomposition = valueDecompositionIfAny.get();
+                val valueAsJson = valueDecomposition.toJson();
+                valueDecomposition.accept(
+                        simple->{
+                            // special treatment for BLOB/CLOB/ENUM as these 
are better represented by a map
+                            if(simple.getType() == ValueType.BLOB
+                                    || simple.getType() == ValueType.CLOB
+                                    || simple.getType() == ValueType.ENUM) {
+                                val decompRepr = 
JsonRepresentation.jsonAsMap(valueAsJson);
+                                repr.mapPutJsonRepresentation("value", 
decompRepr);
+                                appendFormats(repr, null, 
simple.getType().value(), context.isSuppressExtensions());
+                            } else {
+                                // using string representation from value 
semantics
+                                repr.mapPutString("value", valueAsJson);
+                                appendFormats(repr, "string", 
simple.getType().value(), context.isSuppressExtensions());
+                            }
+                        },
+                        tuple->{
+                            val decompRepr = 
JsonRepresentation.jsonAsMap(valueAsJson);
+                            repr.mapPutJsonRepresentation("value", decompRepr);
+
+                            val typeTupleAsFormat = "{"
+                                    + tuple.getElements().stream()
+                                        .map(el->el.getType().value())
+                                        .collect(Collectors.joining(","))
+                                    + "}";
+
+                            appendFormats(repr, null, typeTupleAsFormat, 
context.isSuppressExtensions());
+                        });
+            } else {
+                appendNullAndFormat(repr, context.isSuppressExtensions());
             }
-            return appendNullAndFormat(repr, context.isSuppressExtensions());
         }
     }
 
@@ -188,13 +216,11 @@ public class JsonValueEncoder {
                 .toEncodedString(Format.JSON, 
_Casts.uncheckedCast(adapter.getPojo()));
     }
 
-    @Deprecated
-    static void appendFormats(final JsonRepresentation repr, final String 
format, final String xIsisFormat, final boolean suppressExtensions) {
-        if(format != null) {
-            repr.mapPutString("format", format);
-        }
-        if(!suppressExtensions && xIsisFormat != null) {
-            repr.mapPutString("extensions.x-isis-format", xIsisFormat);
+    static void appendFormats(final JsonRepresentation repr,
+            final @Nullable String format, final @Nullable String 
extendedFormat, final boolean suppressExtensions) {
+        repr.putFormat(format);
+        if(!suppressExtensions) {
+            repr.putExtendedFormat(extendedFormat);
         }
     }
 
diff --git 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectPropertyReprRenderer.java
 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectPropertyReprRenderer.java
index cddfeeb9ab..2d850dc881 100644
--- 
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectPropertyReprRenderer.java
+++ 
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectPropertyReprRenderer.java
@@ -78,7 +78,7 @@ extends AbstractObjectMemberReprRenderer<OneToOneAssociation> 
{
     // value
     // ///////////////////////////////////////////////////
 
-    private Object addValue(final LinkFollowSpecs linkFollower) {
+    private void addValue(final LinkFollowSpecs linkFollower) {
         val valueAdapterIfAny = objectMember.get(objectAdapter, 
getInteractionInitiatedBy());
 
         // use the runtime type if we have a value, otherwise fallback to the 
compile time type of the member
@@ -89,17 +89,18 @@ extends 
AbstractObjectMemberReprRenderer<OneToOneAssociation> {
         val spec = valueAdapter.getSpecification();
 
         if (spec.isValue()) {
-            return jsonValueEncoder
+            jsonValueEncoder
                     .appendValueAndFormat(
                             valueAdapter,
                             representation,
                             JsonValueConverter.Context.of(objectMember, 
resourceContext.suppressMemberExtensions()));
+            return;
         }
 
         if(valueAdapter.getPojo() == null) {
             final NullNode value = NullNode.getInstance();
             representation.mapPutJsonNode("value", value);
-            return value;
+            return;
         }
 
         final boolean eagerlyRender =
@@ -125,7 +126,6 @@ extends 
AbstractObjectMemberReprRenderer<OneToOneAssociation> {
 
         final JsonRepresentation valueJsonRepr = valueLinkBuilder.build();
         representation.mapPutJsonRepresentation("value", valueJsonRepr);
-        return valueJsonRepr;
 
     }
 

Reply via email to