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 da099afcc6 ISIS-3167: ManagedObject:  equals/hashCode/toString 
side-effect-free
da099afcc6 is described below

commit da099afcc6b44e1a679b1aad55f2b0dd2006d2bb
Author: Andi Huber <[email protected]>
AuthorDate: Mon Aug 29 07:56:01 2022 +0200

    ISIS-3167: ManagedObject:  equals/hashCode/toString side-effect-free
---
 .../isis/core/metamodel/object/ManagedObject.java  |  7 +-
 ...ctUnspecified.java => _ManagedObjectEmpty.java} | 50 ++++----------
 .../metamodel/object/_ManagedObjectPacked.java     | 35 ++++------
 .../metamodel/object/_ManagedObjectSpecified.java  | 76 +++++++++++++++++++++-
 ...ied.java => _ManagedObjectSpecifiedLegacy.java} |  2 +-
 .../object/_ManagedObjectUnspecified.java          | 16 +++++
 .../object/_ManagedObjectWithBookmark.java         |  2 +-
 7 files changed, 118 insertions(+), 70 deletions(-)

diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
index 66c9375153..3d965b3416 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
@@ -32,6 +32,7 @@ import 
org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.HasMetaModelContext;
 import org.apache.isis.core.metamodel.facets.object.icon.ObjectIcon;
 import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest;
+import org.apache.isis.core.metamodel.object.ManagedObject.Specialization;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
@@ -163,7 +164,7 @@ public interface ManagedObject extends HasMetaModelContext {
             STATEFUL,
             /** has a stateful pojo, with mutable object reference */
             REFETCHABLE,
-            /** has an unmodifiable collection of pojos; the collection's 
object reference is immutable;
+            /** creates an unmodifiable collection of pojos (lazily);
              * supports unpacking into a {@link Can} of {@link 
ManagedObject}s;*/
             PACKED;
             ////
@@ -175,7 +176,7 @@ public interface ManagedObject extends HasMetaModelContext {
             public boolean isStateful() { return this == STATEFUL; }
             /** has a stateful pojo, with mutable object reference */
             public boolean isRefetchable() { return this == REFETCHABLE; }
-            /** has an unmodifiable collection of pojos; the collection's 
object reference is immutable;
+            /** creates an unmodifiable collection of pojos (lazily);
              * supports unpacking into a {@link Can} of {@link 
ManagedObject}s;*/
             public boolean isPacked() { return this == PACKED; }
         }
@@ -424,7 +425,7 @@ public interface ManagedObject extends HasMetaModelContext {
      */
     static PackedManagedObject packed(
             final @NonNull ObjectSpecification elementSpec,
-            final Can<ManagedObject> nonScalar) {
+            final @Nullable Can<ManagedObject> nonScalar) {
         return new _ManagedObjectPacked(elementSpec, nonScalar);
     }
 
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEmpty.java
similarity index 55%
copy from 
core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
copy to 
core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEmpty.java
index 0a69bafc36..00d32cd097 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEmpty.java
@@ -21,30 +21,18 @@ package org.apache.isis.core.metamodel.object;
 import java.util.Optional;
 import java.util.function.Supplier;
 
-import org.springframework.lang.Nullable;
-
 import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
-/**
- * (package private) specialization corresponding to {@link 
Specialization#UNSPECIFIED}
- * @see ManagedObject.Specialization#UNSPECIFIED
- */
-final class _ManagedObjectUnspecified
-implements ManagedObject {
-
-    static final ManagedObject INSTANCE = new _ManagedObjectUnspecified();
+import lombok.Getter;
 
-    @Override
-    public Specialization getSpecialization() {
-        return Specialization.UNSPECIFIED;
-    }
+@Getter
+final class _ManagedObjectEmpty
+extends _ManagedObjectSpecified {
 
-    @Override
-    public ObjectSpecification getSpecification() {
-        throw _Exceptions.unsupportedOperation();
+    _ManagedObjectEmpty(
+            final ObjectSpecification spec) {
+        super(ManagedObject.Specialization.EMPTY, spec);
     }
 
     @Override
@@ -63,28 +51,12 @@ implements ManagedObject {
     }
 
     @Override
-    public boolean isBookmarkMemoized() {
-        return false;
+    public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) {
     }
 
     @Override
-    public void refreshViewmodel(final @Nullable Supplier<Bookmark> 
bookmarkSupplier) {
-    }
-
-    @Override
-    public MetaModelContext getMetaModelContext() {
-        throw _Exceptions
-                .illegalArgument("Can only retrieve MetaModelContext from 
ManagedObjects "
-                        + "that have an ObjectSpecification.");
-    }
-
-    @Override
-    public Supplier<ManagedObject> asSupplier() {
-        return ()->this;
-    }
-
-    @Override
-    public void assertSpecIsInSyncWithPojo() {
+    public boolean isBookmarkMemoized() {
+        return false;
     }
 
-}
+}
\ No newline at end of file
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectPacked.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectPacked.java
index a62ec2aab4..5438f1a990 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectPacked.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectPacked.java
@@ -27,35 +27,27 @@ import org.springframework.lang.Nullable;
 
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.collections.Can;
-import org.apache.isis.commons.internal.base._Lazy;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 import lombok.NonNull;
-import lombok.ToString;
 
 /**
  * (package private) specialization corresponding to {@link 
Specialization#PACKED}
  * @see ManagedObject.Specialization#PACKED
  */
-@ToString
 final class _ManagedObjectPacked
 extends _ManagedObjectSpecified
 implements PackedManagedObject {
 
-    public _ManagedObjectPacked(
-            final ObjectSpecification elementSpec,
-            final Can<ManagedObject> nonScalar) {
-        super(Specialization.PACKED);
-        this.elementSpec = elementSpec;
-        this.nonScalar = nonScalar;
-    }
-
-    final @NonNull ObjectSpecification elementSpec;
-    final @NonNull Can<ManagedObject> nonScalar;
+    private final @NonNull Can<ManagedObject> nonScalar;
 
-    @Override
-    public ObjectSpecification getSpecification() {
-        return elementSpec;
+    _ManagedObjectPacked(
+            final ObjectSpecification elementSpec,
+            final @Nullable Can<ManagedObject> nonScalar) {
+        super(Specialization.PACKED, elementSpec);
+        this.nonScalar = nonScalar!=null
+                ? nonScalar
+                : Can.empty();
     }
 
     @Override
@@ -67,24 +59,19 @@ implements PackedManagedObject {
                 .collect(Collectors.toList()));
     }
 
-    private final _Lazy<Optional<Bookmark>> bookmarkLazy =
-            _Lazy.threadSafe(()->{
-                return 
Optional.of(getSpecification().getMetaModelContext().getObjectManager().bookmarkObject(this));
-            });
-
     @Override
     public Optional<Bookmark> getBookmark() {
-        return bookmarkLazy.get();
+        return Optional.empty();
     }
 
     @Override
     public Optional<Bookmark> getBookmarkRefreshed() {
-        return getBookmark(); // no-effect
+        return Optional.empty();
     }
 
     @Override
     public boolean isBookmarkMemoized() {
-        return bookmarkLazy.isMemoized();
+        return false;
     }
 
     @Override
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
index 2db5880d06..5b351e01b0 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
@@ -18,12 +18,19 @@
  */
 package org.apache.isis.core.metamodel.object;
 
+import java.util.Objects;
 import java.util.function.Supplier;
 
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.commons.internal.assertions._Assert;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 import lombok.Getter;
+import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
+import lombok.val;
 import lombok.experimental.Accessors;
 
 @RequiredArgsConstructor
@@ -31,7 +38,10 @@ abstract class _ManagedObjectSpecified
 implements ManagedObject {
 
     @Getter(onMethod_ = {@Override}) @Accessors(makeFinal = true)
-    private final Specialization specialization;
+    private final @NonNull Specialization specialization;
+
+    @Getter(onMethod_ = {@Override}) @Accessors(makeFinal = true)
+    private final @NonNull ObjectSpecification specification;
 
     @Override
     public final MetaModelContext getMetaModelContext() {
@@ -45,7 +55,7 @@ implements ManagedObject {
 
     /** debug */
     @Override
-    public void assertSpecIsInSyncWithPojo() {
+    public final void assertSpecIsInSyncWithPojo() {
 //        val pojo = getPojo();
 //        val spec = getSpecification();
 //        if(pojo==null
@@ -59,4 +69,66 @@ implements ManagedObject {
         //_Assert.assertEquals(spec, actualSpec);
     }
 
+    //XXX compares pojos by their 'equals' semantics -
+    // note though: some value-types have an explicit order-relation which 
could potentially say differently
+    @Override
+    public final boolean equals(final Object obj) {
+        // make sure equals(Object) is without side-effects!
+        if(this == obj) {
+            return true;
+        }
+        if(!(obj instanceof ManagedObject)) {
+            return false;
+        }
+        val other = (ManagedObject)obj;
+        if(!this.getSpecialization().equals(other.getSpecialization())) {
+            return false;
+        }
+        if(!this.getSpecification().equals(other.getSpecification())) {
+            return false;
+        }
+        val canGetPojosWithoutSideeffect = 
!this.getSpecialization().getPojoPolicy().isRefetchable();
+        if(canGetPojosWithoutSideeffect) {
+            // expected to work for packed variant just fine, as it compares 
lists
+            return Objects.equals(this.getPojo(), other.getPojo());
+        }
+        // objects are considered equal if their bookmarks match
+        _Assert.assertTrue(other.isBookmarkMemoized()); // guarantee no 
side-effects on other
+        return Objects.equals(
+                sideEffectFreeBookmark(),
+                
other.getBookmark().orElseThrow(_Exceptions::unexpectedCodeReach));
+    }
+
+    @Override
+    public final int hashCode() {
+        // make sure hashCode() is without side-effects!
+        val canGetPojosWithoutSideeffect = 
!getSpecialization().getPojoPolicy().isRefetchable();
+        return canGetPojosWithoutSideeffect
+                // expected to work for packed variant just fine, as it 
compares lists
+                ? Objects.hash(getSpecification().getCorrespondingClass(), 
getPojo())
+                : Objects.hash(getSpecification().getCorrespondingClass(), 
sideEffectFreeBookmark());
+    }
+
+    @Override
+    public final String toString() {
+        // make sure toString() is without side-effects!
+        return String.format("ManagedObject(%s, spec=%s, pojo=%s)",
+                getSpecialization().name(),
+                getSpecification(),
+                !getSpecialization().getPojoPolicy().isRefetchable()
+                    ? getPojo() // its safe to get pojo side-effect free
+                    : !getSpecialization().getBookmarkPolicy().isNoBookmark()
+                        ? String.format("(refetchable, %s)", 
sideEffectFreeBookmark())
+                        : "(refetchable, suppressed to not cause side 
effects)");
+    }
+
+    // -- HELPER
+
+    private Bookmark sideEffectFreeBookmark() {
+        
_Assert.assertFalse(getSpecialization().getBookmarkPolicy().isNoBookmark());
+        _Assert.assertTrue(isBookmarkMemoized());
+        return getBookmark().orElseThrow(_Exceptions::unexpectedCodeReach);
+    }
+
+
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecifiedLegacy.java
similarity index 97%
copy from 
core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
copy to 
core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecifiedLegacy.java
index 2db5880d06..b170889113 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecifiedLegacy.java
@@ -27,7 +27,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.experimental.Accessors;
 
 @RequiredArgsConstructor
-abstract class _ManagedObjectSpecified
+abstract class _ManagedObjectSpecifiedLegacy
 implements ManagedObject {
 
     @Getter(onMethod_ = {@Override}) @Accessors(makeFinal = true)
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
index 0a69bafc36..e26f76eeff 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectUnspecified.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.core.metamodel.object;
 
+import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Supplier;
 
@@ -87,4 +88,19 @@ implements ManagedObject {
     public void assertSpecIsInSyncWithPojo() {
     }
 
+    @Override
+    public boolean equals(final Object other) {
+        return Objects.equals(this, other);
+    }
+
+    @Override
+    public int hashCode() {
+        return 0;
+    }
+
+    @Override
+    public final String toString() {
+        return "ManagedObject(UNSPECIFIED)";
+    }
+
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java
index 1955374cbd..b928865fde 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java
@@ -37,7 +37,7 @@ import lombok.NonNull;
 import lombok.val;
 
 abstract class _ManagedObjectWithBookmark
-extends _ManagedObjectSpecified {
+extends _ManagedObjectSpecifiedLegacy {
 
     protected final _Lazy<Optional<Bookmark>> bookmarkLazy =
             _Lazy.threadSafe(()->bookmark(this));

Reply via email to