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));