Data objects: add support for DO entity contributions
DO entity contributions allow to add payload in form of a specific DO
entity to an existing DO entity. The payload is part of an internal
attribute node _contributions. The contributions are designed in a way
that it is known which contribution are applicable to which DO entities
in order to determine a list of DO entities that can be held within
another DO entity.
diff --git a/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/DataObjectContributionTest.java b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/DataObjectContributionTest.java
new file mode 100644
index 0000000..e2f1a73
--- /dev/null
+++ b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/DataObjectContributionTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.dataobject;
+
+import static org.junit.Assert.*;
+
+import org.eclipse.scout.rt.dataobject.fixture.DoubleContributionFixtureDo;
+import org.eclipse.scout.rt.dataobject.fixture.EntityFixtureDo;
+import org.eclipse.scout.rt.dataobject.fixture.FirstSimpleContributionFixtureDo;
+import org.eclipse.scout.rt.dataobject.fixture.ProjectContributionFixtureDo;
+import org.eclipse.scout.rt.dataobject.fixture.ProjectFixtureDo;
+import org.eclipse.scout.rt.dataobject.fixture.ScoutContributionFixtureDo;
+import org.eclipse.scout.rt.dataobject.fixture.ScoutFixtureDo;
+import org.eclipse.scout.rt.dataobject.fixture.SecondSimpleContributionFixtureDo;
+import org.eclipse.scout.rt.dataobject.fixture.SimpleFixtureDo;
+import org.eclipse.scout.rt.platform.BEANS;
+import org.eclipse.scout.rt.platform.util.Assertions.AssertionException;
+import org.eclipse.scout.rt.testing.platform.runner.PlatformTestRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(PlatformTestRunner.class)
+public class DataObjectContributionTest {
+
+ @Test
+ public void testHasGetContribution() {
+ SimpleFixtureDo doEntity = BEANS.get(SimpleFixtureDo.class);
+ assertFalse(doEntity.has(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME)); // node doesn't exist
+ assertTrue(doEntity.getContributions().isEmpty());
+
+ assertThrows(AssertionException.class, () -> doEntity.getContribution(null)); // contribution class is mandatory
+
+ // has -> false, get -> null
+ assertFalse(doEntity.hasContribution(FirstSimpleContributionFixtureDo.class));
+ assertFalse(doEntity.hasContribution(SecondSimpleContributionFixtureDo.class));
+ assertNull(doEntity.getContribution(FirstSimpleContributionFixtureDo.class));
+ assertNull(doEntity.getContribution(SecondSimpleContributionFixtureDo.class));
+
+ // add first contribution
+ FirstSimpleContributionFixtureDo firstContribution = BEANS.get(FirstSimpleContributionFixtureDo.class);
+ doEntity.putContribution(firstContribution);
+
+ // check node availability and return values of has/get
+ assertEquals(1, doEntity.getContributions().size());
+ assertSame(firstContribution, doEntity.getContributions().iterator().next());
+ assertTrue(doEntity.has(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME));
+ assertTrue(doEntity.hasContribution(FirstSimpleContributionFixtureDo.class));
+ assertSame(firstContribution, doEntity.getContribution(FirstSimpleContributionFixtureDo.class));
+
+ // second contribution still not available
+ assertFalse(doEntity.hasContribution(SecondSimpleContributionFixtureDo.class));
+ assertNull(doEntity.getContribution(SecondSimpleContributionFixtureDo.class));
+ }
+
+ @Test
+ public void testContribution() {
+ SimpleFixtureDo doEntity = BEANS.get(SimpleFixtureDo.class);
+ assertNull(doEntity.getContribution(FirstSimpleContributionFixtureDo.class));
+ FirstSimpleContributionFixtureDo firstContribution = doEntity.contribution(FirstSimpleContributionFixtureDo.class);
+ assertNotNull(firstContribution);
+ assertSame(firstContribution, doEntity.contribution(FirstSimpleContributionFixtureDo.class)); // same instance if contribution is already available (via previous getOrCreate call)
+ assertSame(firstContribution, doEntity.getContribution(FirstSimpleContributionFixtureDo.class)); // same instance for get call
+
+ SecondSimpleContributionFixtureDo secondContribution = BEANS.get(SecondSimpleContributionFixtureDo.class);
+ doEntity.putContribution(secondContribution);
+ assertSame(secondContribution, doEntity.contribution(SecondSimpleContributionFixtureDo.class)); // same instance if contribution is already available (via putContribution)
+ }
+
+ @Test
+ public void testPutContribution() {
+ SimpleFixtureDo doEntity = BEANS.get(SimpleFixtureDo.class);
+ FirstSimpleContributionFixtureDo firstContribution1 = BEANS.get(FirstSimpleContributionFixtureDo.class);
+ doEntity.putContribution(firstContribution1);
+ assertEquals(1, doEntity.getContributions().size());
+ FirstSimpleContributionFixtureDo firstContribution2 = BEANS.get(FirstSimpleContributionFixtureDo.class);
+ doEntity.putContribution(firstContribution2);
+ assertEquals(1, doEntity.getContributions().size()); // size is still 1, first contribution was overridden
+
+ assertSame(firstContribution2, doEntity.getContribution(FirstSimpleContributionFixtureDo.class)); // same as 2. instance
+ }
+
+ @Test
+ public void testRemoveContribution() {
+ SimpleFixtureDo doEntity = BEANS.get(SimpleFixtureDo.class);
+ assertFalse(doEntity.removeContribution(FirstSimpleContributionFixtureDo.class)); // no effect
+ assertFalse(doEntity.removeContribution(SecondSimpleContributionFixtureDo.class)); // no effect
+ assertFalse(doEntity.has(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME)); // node doesn't exist
+
+ doEntity.putContribution(BEANS.get(FirstSimpleContributionFixtureDo.class));
+ assertTrue(doEntity.has(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME));
+ doEntity.putContribution(BEANS.get(SecondSimpleContributionFixtureDo.class));
+
+ assertTrue(doEntity.removeContribution(FirstSimpleContributionFixtureDo.class));
+ assertTrue(doEntity.has(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME));
+
+ assertTrue(doEntity.removeContribution(SecondSimpleContributionFixtureDo.class));
+ assertFalse(doEntity.has(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME)); // node is removed after last contribution is removed
+ }
+
+ @Test
+ public void testValidation() {
+ SimpleFixtureDo simpleFixture = BEANS.get(SimpleFixtureDo.class);
+ assertThrows(AssertionException.class, () -> simpleFixture.validateContributionClass(null)); // missing contribution class
+ simpleFixture.validateContributionClass(FirstSimpleContributionFixtureDo.class);
+ simpleFixture.validateContributionClass(SecondSimpleContributionFixtureDo.class);
+ assertThrows(AssertionException.class, () -> simpleFixture.validateContributionClass(ScoutContributionFixtureDo.class));
+ assertThrows(AssertionException.class, () -> simpleFixture.validateContributionClass(ProjectContributionFixtureDo.class));
+
+ // not using BEANS.get because bean is replaced by ProjectFixtureDo (only for validation, not a real case this way)
+ ScoutFixtureDo scoutFixture = new ScoutFixtureDo();
+ assertThrows(AssertionException.class, () -> scoutFixture.validateContributionClass(FirstSimpleContributionFixtureDo.class));
+ assertThrows(AssertionException.class, () -> scoutFixture.validateContributionClass(SecondSimpleContributionFixtureDo.class));
+ assertThrows(AssertionException.class, () -> scoutFixture.validateContributionClass(ProjectContributionFixtureDo.class));
+ scoutFixture.validateContributionClass(ScoutContributionFixtureDo.class);
+
+ // using subclasses data object
+ ProjectFixtureDo projectFixture = BEANS.get(ProjectFixtureDo.class);
+ assertThrows(AssertionException.class, () -> projectFixture.validateContributionClass(FirstSimpleContributionFixtureDo.class));
+ assertThrows(AssertionException.class, () -> projectFixture.validateContributionClass(SecondSimpleContributionFixtureDo.class));
+ projectFixture.validateContributionClass(ScoutContributionFixtureDo.class);
+ projectFixture.validateContributionClass(ProjectContributionFixtureDo.class);
+
+ // verify contribution DO with two containers
+ projectFixture.validateContributionClass(DoubleContributionFixtureDo.class);
+ simpleFixture.validateContributionClass(DoubleContributionFixtureDo.class);
+ assertThrows(AssertionException.class, () -> BEANS.get(EntityFixtureDo.class).validateContributionClass(SecondSimpleContributionFixtureDo.class));
+
+ // verify that all methods check validation
+ assertThrows(AssertionException.class, () -> simpleFixture.getContribution(ScoutContributionFixtureDo.class));
+ assertThrows(AssertionException.class, () -> simpleFixture.putContribution(BEANS.get(ScoutContributionFixtureDo.class)));
+ assertThrows(AssertionException.class, () -> simpleFixture.contribution(ScoutContributionFixtureDo.class));
+ assertThrows(AssertionException.class, () -> simpleFixture.hasContribution(ScoutContributionFixtureDo.class));
+ assertThrows(AssertionException.class, () -> simpleFixture.removeContribution(ScoutContributionFixtureDo.class));
+ }
+
+ @Test
+ public void testEquality() {
+ // Add contributions in order first -> second
+ SimpleFixtureDo doEntity1 = BEANS.get(SimpleFixtureDo.class);
+ doEntity1.putContribution(BEANS.get(FirstSimpleContributionFixtureDo.class));
+ doEntity1.putContribution(BEANS.get(SecondSimpleContributionFixtureDo.class));
+
+ // Add contributions in order second -> first
+ SimpleFixtureDo doEntity2 = BEANS.get(SimpleFixtureDo.class);
+ doEntity2.putContribution(BEANS.get(SecondSimpleContributionFixtureDo.class));
+ doEntity2.putContribution(BEANS.get(FirstSimpleContributionFixtureDo.class));
+
+ // Order of contributions must not be relevant for comparison
+ assertEquals(doEntity1, doEntity2);
+ }
+
+ @Test
+ public void testSerialization() {
+ IPrettyPrintDataObjectMapper mapper = BEANS.get(IPrettyPrintDataObjectMapper.class);
+
+ SimpleFixtureDo doEntity = BEANS.get(SimpleFixtureDo.class)
+ .withName1("name");
+
+ FirstSimpleContributionFixtureDo firstContribution = BEANS.get(FirstSimpleContributionFixtureDo.class).withFirstValue("first-value");
+ doEntity.putContribution(firstContribution);
+ SecondSimpleContributionFixtureDo secondContribution = BEANS.get(SecondSimpleContributionFixtureDo.class).withSecondValue("second-value");
+ doEntity.putContribution(secondContribution);
+
+ String json = mapper.writeValue(doEntity);
+
+ assertEquals("{\n"
+ + " \"_type\" : \"scout.SimpleFixture\",\n"
+ + " \"_contributions\" : [ {\n"
+ + " \"_type\" : \"scout.FirstSimpleContributionFixture\",\n"
+ + " \"firstValue\" : \"first-value\"\n"
+ + " }, {\n"
+ + " \"_type\" : \"scout.SecondSimpleContributionFixture\",\n"
+ + " \"secondValue\" : \"second-value\"\n"
+ + " } ],\n"
+ + " \"name1\" : \"name\"\n"
+ + "}", json.replaceAll("\\r", ""));
+
+ SimpleFixtureDo deserializedDoEntity = mapper.readValue(json, SimpleFixtureDo.class);
+ assertEquals("name", deserializedDoEntity.getName1());
+ assertEquals(2, deserializedDoEntity.getContributions().size());
+ assertEquals(firstContribution, deserializedDoEntity.getContribution(FirstSimpleContributionFixtureDo.class));
+ assertEquals(secondContribution, deserializedDoEntity.getContribution(SecondSimpleContributionFixtureDo.class));
+ assertEquals(doEntity, deserializedDoEntity);
+ }
+}
diff --git a/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/DoNodeTest.java b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/DoNodeTest.java
index f78c951..8d715cb 100644
--- a/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/DoNodeTest.java
+++ b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/DoNodeTest.java
@@ -10,16 +10,12 @@
*/
package org.eclipse.scout.rt.dataobject;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.util.Optional;
-import org.eclipse.scout.rt.dataobject.DoNode;
+import org.eclipse.scout.rt.platform.holders.StringHolder;
import org.junit.Test;
/**
@@ -69,6 +65,23 @@
}
@Test
+ public void testIfPresent() {
+ FixtureDoNode<String> node = new FixtureDoNode<>();
+ assertFalse(node.exists());
+ node.ifPresent(value -> fail("node exists"));
+
+ node.create(); // node with null value
+ StringHolder holder = new StringHolder("other");
+ node.ifPresent(holder::setValue);
+ assertNull(holder.getValue()); // value was set
+
+ node.set("foo"); // node was created and contains a value
+ holder = new StringHolder("other");
+ node.ifPresent(holder::setValue);
+ assertEquals("foo", holder.getValue());
+ }
+
+ @Test
public void testEqualsHashCode() {
FixtureDoNode<String> node1 = new FixtureDoNode<>();
FixtureDoNode<String> node2 = new FixtureDoNode<>();
diff --git a/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/DoubleContributionFixtureDo.java b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/DoubleContributionFixtureDo.java
new file mode 100644
index 0000000..a2dfe7f
--- /dev/null
+++ b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/DoubleContributionFixtureDo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.dataobject.fixture;
+
+import javax.annotation.Generated;
+
+import org.eclipse.scout.rt.dataobject.ContributesTo;
+import org.eclipse.scout.rt.dataobject.DoEntity;
+import org.eclipse.scout.rt.dataobject.DoValue;
+import org.eclipse.scout.rt.dataobject.IDoEntityContribution;
+import org.eclipse.scout.rt.dataobject.TypeName;
+
+@TypeName("scout.DoubleContributionFixture")
+@ContributesTo({SimpleFixtureDo.class, ScoutFixtureDo.class})
+public final class DoubleContributionFixtureDo extends DoEntity implements IDoEntityContribution {
+
+ public DoValue<String> value() {
+ return doValue("value");
+ }
+
+ /* **************************************************************************
+ * GENERATED CONVENIENCE METHODS
+ * *************************************************************************/
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public DoubleContributionFixtureDo withValue(String value) {
+ value().set(value);
+ return this;
+ }
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public String getValue() {
+ return value().get();
+ }
+}
diff --git a/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/FirstSimpleContributionFixtureDo.java b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/FirstSimpleContributionFixtureDo.java
new file mode 100644
index 0000000..e8c5233
--- /dev/null
+++ b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/FirstSimpleContributionFixtureDo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.dataobject.fixture;
+
+import javax.annotation.Generated;
+
+import org.eclipse.scout.rt.dataobject.ContributesTo;
+import org.eclipse.scout.rt.dataobject.DoEntity;
+import org.eclipse.scout.rt.dataobject.DoValue;
+import org.eclipse.scout.rt.dataobject.IDoEntityContribution;
+import org.eclipse.scout.rt.dataobject.TypeName;
+
+@TypeName("scout.FirstSimpleContributionFixture")
+@ContributesTo(SimpleFixtureDo.class)
+public final class FirstSimpleContributionFixtureDo extends DoEntity implements IDoEntityContribution {
+
+ public DoValue<String> firstValue() {
+ return doValue("firstValue");
+ }
+
+ /* **************************************************************************
+ * GENERATED CONVENIENCE METHODS
+ * *************************************************************************/
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public FirstSimpleContributionFixtureDo withFirstValue(String firstValue) {
+ firstValue().set(firstValue);
+ return this;
+ }
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public String getFirstValue() {
+ return firstValue().get();
+ }
+}
diff --git a/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/ProjectContributionFixtureDo.java b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/ProjectContributionFixtureDo.java
new file mode 100644
index 0000000..f15a1b5
--- /dev/null
+++ b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/ProjectContributionFixtureDo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.dataobject.fixture;
+
+import javax.annotation.Generated;
+
+import org.eclipse.scout.rt.dataobject.ContributesTo;
+import org.eclipse.scout.rt.dataobject.DoEntity;
+import org.eclipse.scout.rt.dataobject.DoValue;
+import org.eclipse.scout.rt.dataobject.IDoEntityContribution;
+import org.eclipse.scout.rt.dataobject.TypeName;
+
+@TypeName("scout.ProjectContributionFixture")
+@ContributesTo(ProjectFixtureDo.class)
+public final class ProjectContributionFixtureDo extends DoEntity implements IDoEntityContribution {
+
+ public DoValue<String> value() {
+ return doValue("value");
+ }
+
+ /* **************************************************************************
+ * GENERATED CONVENIENCE METHODS
+ * *************************************************************************/
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public ProjectContributionFixtureDo withValue(String value) {
+ value().set(value);
+ return this;
+ }
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public String getValue() {
+ return value().get();
+ }
+}
diff --git a/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/ScoutContributionFixtureDo.java b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/ScoutContributionFixtureDo.java
new file mode 100644
index 0000000..b3581e1
--- /dev/null
+++ b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/ScoutContributionFixtureDo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.dataobject.fixture;
+
+import javax.annotation.Generated;
+
+import org.eclipse.scout.rt.dataobject.ContributesTo;
+import org.eclipse.scout.rt.dataobject.DoEntity;
+import org.eclipse.scout.rt.dataobject.DoValue;
+import org.eclipse.scout.rt.dataobject.IDoEntityContribution;
+import org.eclipse.scout.rt.dataobject.TypeName;
+
+@TypeName("scout.ScoutContributionFixture")
+@ContributesTo(ScoutFixtureDo.class)
+public final class ScoutContributionFixtureDo extends DoEntity implements IDoEntityContribution {
+
+ public DoValue<String> value() {
+ return doValue("value");
+ }
+
+ /* **************************************************************************
+ * GENERATED CONVENIENCE METHODS
+ * *************************************************************************/
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public ScoutContributionFixtureDo withValue(String value) {
+ value().set(value);
+ return this;
+ }
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public String getValue() {
+ return value().get();
+ }
+}
diff --git a/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/SecondSimpleContributionFixtureDo.java b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/SecondSimpleContributionFixtureDo.java
new file mode 100644
index 0000000..4d94961
--- /dev/null
+++ b/org.eclipse.scout.rt.dataobject.test/src/test/java/org/eclipse/scout/rt/dataobject/fixture/SecondSimpleContributionFixtureDo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.dataobject.fixture;
+
+import javax.annotation.Generated;
+
+import org.eclipse.scout.rt.dataobject.ContributesTo;
+import org.eclipse.scout.rt.dataobject.DoEntity;
+import org.eclipse.scout.rt.dataobject.DoValue;
+import org.eclipse.scout.rt.dataobject.IDoEntityContribution;
+import org.eclipse.scout.rt.dataobject.TypeName;
+
+@TypeName("scout.SecondSimpleContributionFixture")
+@ContributesTo(SimpleFixtureDo.class)
+public final class SecondSimpleContributionFixtureDo extends DoEntity implements IDoEntityContribution {
+
+ public DoValue<String> secondValue() {
+ return doValue("secondValue");
+ }
+
+ /* **************************************************************************
+ * GENERATED CONVENIENCE METHODS
+ * *************************************************************************/
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public SecondSimpleContributionFixtureDo withSecondValue(String secondValue) {
+ secondValue().set(secondValue);
+ return this;
+ }
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public String getSecondValue() {
+ return secondValue().get();
+ }
+}
diff --git a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/ContributesTo.java b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/ContributesTo.java
new file mode 100644
index 0000000..8f96db8
--- /dev/null
+++ b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/ContributesTo.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.dataobject;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used for DO entity contributions
+ *
+ * @see IDoEntityContribution
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface ContributesTo {
+
+ Class<? extends IDoEntity>[] value();
+}
diff --git a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoEntity.java b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoEntity.java
index 1eac9cd..194a920 100644
--- a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoEntity.java
+++ b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoEntity.java
@@ -10,7 +10,7 @@
*/
package org.eclipse.scout.rt.dataobject;
-import static org.eclipse.scout.rt.platform.util.Assertions.assertNotNull;
+import static org.eclipse.scout.rt.platform.util.Assertions.*;
import java.util.Collection;
import java.util.Collections;
@@ -22,6 +22,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Stream;
import org.eclipse.scout.rt.platform.BEANS;
import org.eclipse.scout.rt.platform.util.Assertions;
@@ -50,6 +51,11 @@
*/
public class DoEntity implements IDoEntity {
+ /**
+ * Attribute uses a {@link DoCollection} internally because order of elements is not relevant for equality.
+ */
+ public static final String CONTRIBUTIONS_ATTRIBUTE_NAME = "_contributions";
+
private final Map<String, DoNode<?>> m_attributes = new LinkedHashMap<>();
/**
@@ -196,6 +202,103 @@
}
@Override
+ public Collection<IDoEntityContribution> getContributions() {
+ if (!has(CONTRIBUTIONS_ATTRIBUTE_NAME)) {
+ return Collections.emptyList();
+ }
+
+ return Collections.unmodifiableCollection(getContributionsInternal());
+ }
+
+ @Override
+ public <CONTRIBUTION extends IDoEntityContribution> CONTRIBUTION getContribution(Class<CONTRIBUTION> contributionClass) {
+ validateContributionClass(contributionClass);
+ if (!has(CONTRIBUTIONS_ATTRIBUTE_NAME)) {
+ return null;
+ }
+
+ return getContributionsInternal().stream()
+ .filter(contribution -> contributionClass.equals(contribution.getClass()))
+ .findFirst()
+ .map(contributionClass::cast)
+ .orElse(null);
+ }
+
+ @Override
+ public <CONTRIBUTION extends IDoEntityContribution> void putContribution(CONTRIBUTION contribution) {
+ assertNotNull(contribution, "contribution is required");
+ validateContributionClass(contribution.getClass());
+
+ removeContribution(contribution.getClass());
+ ensureContributionsNode();
+ getContributionsInternal().add(contribution);
+ }
+
+ @Override
+ public <CONTRIBUTION extends IDoEntityContribution> CONTRIBUTION contribution(Class<CONTRIBUTION> contributionClass) {
+ validateContributionClass(contributionClass);
+
+ if (!hasContribution(contributionClass)) {
+ CONTRIBUTION contribution = BEANS.get(contributionClass);
+ putContribution(contribution);
+ return contribution;
+ }
+
+ return getContribution(contributionClass);
+ }
+
+ @Override
+ public boolean hasContribution(Class<? extends IDoEntityContribution> contributionClass) {
+ validateContributionClass(contributionClass);
+ return getContribution(contributionClass) != null;
+ }
+
+ @Override
+ public boolean removeContribution(Class<? extends IDoEntityContribution> contributionClass) {
+ validateContributionClass(contributionClass);
+ if (!has(CONTRIBUTIONS_ATTRIBUTE_NAME)) {
+ return false;
+ }
+
+ Collection<IDoEntityContribution> list = getContributionsInternal();
+ boolean removed = list.removeIf(contribution -> contributionClass.equals(contribution.getClass()));
+ if (list.isEmpty()) {
+ remove(CONTRIBUTIONS_ATTRIBUTE_NAME);
+ }
+ return removed;
+ }
+
+ protected void validateContributionClass(Class<? extends IDoEntityContribution> contributionClass) {
+ assertNotNull(contributionClass, "contributionClass is required");
+ ContributesTo contributesToAnn = contributionClass.getAnnotation(ContributesTo.class);
+ assertTrue(contributesToAnn != null && contributesToAnn.value() != null, "Contribution class {} is missing a valid {} annotation", contributionClass, ContributesTo.class.getSimpleName());
+ Class<? extends IDoEntity>[] containers = contributesToAnn.value();
+ assertTrue(Stream.of(containers).anyMatch(containerClass -> containerClass.isInstance(this)), "{} is not a valid container class of {}", this.getClass().getSimpleName(), contributionClass.getSimpleName());
+ }
+
+ /**
+ * Ensures that the contributions node ({@link #CONTRIBUTIONS_ATTRIBUTE_NAME}) exists (i.e. creates it if it doesn't
+ * exist yet).
+ */
+ protected void ensureContributionsNode() {
+ if (!has(CONTRIBUTIONS_ATTRIBUTE_NAME)) {
+ putNode(CONTRIBUTIONS_ATTRIBUTE_NAME, new DoCollection<IDoEntityContribution>());
+ }
+ }
+
+ /**
+ * Only call this method if the attribute node is available (e.g. call {@link #ensureContributionsNode()} before if
+ * desired or check for node existance manually).
+ *
+ * @return A mutable collection of DO entity contribution of corresponding {@link DoCollection} node.
+ */
+ protected Collection<IDoEntityContribution> getContributionsInternal() {
+ assertTrue(has(CONTRIBUTIONS_ATTRIBUTE_NAME), "Attribute node for DO entity contributions is missing");
+ //noinspection unchecked
+ return ((DoCollection<IDoEntityContribution>) Assertions.assertType(getNode(CONTRIBUTIONS_ATTRIBUTE_NAME), DoCollection.class)).get();
+ }
+
+ @Override
public int hashCode() {
final int prime = 31;
int result = 1;
diff --git a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoList.java b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoList.java
index 228e5ee..7646066 100644
--- a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoList.java
+++ b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoList.java
@@ -22,7 +22,7 @@
* @see DoEntity#doList(String) creator method
*/
@SuppressWarnings("squid:S2333") // redundant final
-public final class DoList<V> extends AbstractDoCollection<V, List<V>> implements IDataObject {
+public final class DoList<V> extends AbstractDoCollection<V, List<V>> {
public DoList() {
this(null, null);
diff --git a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoNode.java b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoNode.java
index 50eba8e..cd76f72 100644
--- a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoNode.java
+++ b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/DoNode.java
@@ -78,6 +78,15 @@
}
/**
+ * Calls {@link Consumer#accept(Object)} if node exists.
+ */
+ public final void ifPresent(Consumer<T> consumer) {
+ if (exists()) {
+ consumer.accept(get());
+ }
+ }
+
+ /**
* Internal method used to set attribute name when the node is added to a {@link DoEntity} object.
*/
protected final void setAttributeName(String attributeName) {
diff --git a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDataObject.java b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDataObject.java
index e03098c..e38081d 100644
--- a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDataObject.java
+++ b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDataObject.java
@@ -14,7 +14,8 @@
* Marker interface for a data object.
* <p>
* Object-like data objects are represented using {@link IDoEntity} interface, {@link DoEntity} default implementation
- * and its subclasses, collection-like data objects are represented using {@link DoList}.
+ * and its subclasses, collection-like data objects are represented using {@link DoList}, {@link DoSet} or
+ * {@link DoCollection}.
* <p>
* Use this interface for deserialize any data object, whose content is not further specified, e.g:
*
diff --git a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoCollection.java b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoCollection.java
index b4a52a9..12d10f5 100644
--- a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoCollection.java
+++ b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoCollection.java
@@ -20,7 +20,7 @@
/**
* Interface for a generic collection of values of type {@code V} inside a {@link DoEntity} object.
*/
-public interface IDoCollection<V, COLLECTION extends Collection<V>> extends Iterable<V> {
+public interface IDoCollection<V, COLLECTION extends Collection<V>> extends IDataObject, Iterable<V> {
/**
* @return {@code true} if this attribute is part of a {@link DoEntity}, otherwise {@code false}.
@@ -30,7 +30,7 @@
/**
* @return modifiable collection of all items, never {@code null}.
*/
- public COLLECTION get();
+ COLLECTION get();
/**
* Returns <code>true</code> if this collection contains the item, <code>false</code> otherwise.
diff --git a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoEntity.java b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoEntity.java
index a58a961..64975f1 100644
--- a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoEntity.java
+++ b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoEntity.java
@@ -323,4 +323,48 @@
default boolean isEmpty() {
return allNodes().isEmpty();
}
+
+ /**
+ * Doesn't create the internal contributions node if there is none yet.
+ *
+ * @return An immutable collection of DO entity contributions or an empty list if there a none.
+ */
+ Collection<IDoEntityContribution> getContributions();
+
+ /**
+ * Doesn't create the internal contributions node if there is none yet.
+ *
+ * @return DO entity contribution for this contribution class if available, <code>null</code> otherwise.
+ */
+ <CONTRIBUTION extends IDoEntityContribution> CONTRIBUTION getContribution(Class<CONTRIBUTION> contributionClass);
+
+ /**
+ * Adds a new DO entity contribution. An existing contribution for the same contribution class is overridden.
+ *
+ * @param contribution
+ * Contribution to add.
+ */
+ <CONTRIBUTION extends IDoEntityContribution> void putContribution(CONTRIBUTION contribution);
+
+ /**
+ * @return Existing DO entity contribution for this contribution class if available, otherwise creates a new DO entity
+ * contribution instance, adds it to the contributions and returns it.
+ */
+ <CONTRIBUTION extends IDoEntityContribution> CONTRIBUTION contribution(Class<CONTRIBUTION> contributionClass);
+
+ /**
+ * Doesn't create the internal contributions node if there is none yet.
+ *
+ * @return <code>true</code> if the DO entity contribution for this contribution class is available,
+ * <code>false</code> otherwise.
+ */
+ boolean hasContribution(Class<? extends IDoEntityContribution> contributionClass);
+
+ /**
+ * The internal contribution node is removed if there are no remaining contributions after removal of the given
+ * contribution.
+ *
+ * @return <code>true</code> if the DO entity contribution was available and removed, <code>false</code> otherwise.
+ */
+ boolean removeContribution(Class<? extends IDoEntityContribution> contributionClass);
}
diff --git a/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoEntityContribution.java b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoEntityContribution.java
new file mode 100644
index 0000000..9a29b5d
--- /dev/null
+++ b/org.eclipse.scout.rt.dataobject/src/main/java/org/eclipse/scout/rt/dataobject/IDoEntityContribution.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.dataobject;
+
+/**
+ * Interface for DO entity contributions.
+ * <ul>
+ * <li>Each implementation must be final (no subclassing of contributions).
+ * <li>Each implementation must have a {@link ContributesTo} annotation with a non-empty {@link ContributesTo#value()}
+ * attribute.
+ * </ul>
+ */
+public interface IDoEntityContribution extends IDoEntity {
+}
diff --git a/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/JsonDataObjectsRawSerializationTest.java b/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/JsonDataObjectsRawSerializationTest.java
index 65ad5a4..f9f17f5 100644
--- a/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/JsonDataObjectsRawSerializationTest.java
+++ b/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/JsonDataObjectsRawSerializationTest.java
@@ -73,6 +73,12 @@
}
@Test
+ public void testDoEntityWithContributions() {
+ DoEntity doEntity = (DoEntity) testRawDataObjectMapper("TestDoEntityWithContributions.json");
+ assertTrue(doEntity.getNode(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME) instanceof DoCollection); // even when using raw deserialization, contributions node is always DoCollection
+ }
+
+ @Test
public void testVersionedDo() {
TestVersionedDo versioned = BEANS.get(TestVersionedDo.class).withName("lorem");
String json = s_dataObjectMapper.writeValue(versioned);
@@ -106,10 +112,11 @@
assertEquals("str2", ((DoEntity) item).get("stringAttribute"));
}
- protected void testRawDataObjectMapper(String jsonFileName) {
+ protected IDataObject testRawDataObjectMapper(String jsonFileName) {
String json = readResourceAsString(jsonFileName);
IDataObject object = s_dataObjectMapper.readValueRaw(json);
assertNoTypes(object);
+ return object;
}
/**
diff --git a/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/JsonDataObjectsSerializationTest.java b/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/JsonDataObjectsSerializationTest.java
index 19ef06a..35980a0 100644
--- a/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/JsonDataObjectsSerializationTest.java
+++ b/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/JsonDataObjectsSerializationTest.java
@@ -48,6 +48,7 @@
import org.eclipse.scout.rt.dataobject.IDoEntity;
import org.eclipse.scout.rt.dataobject.IValueFormatConstants;
import org.eclipse.scout.rt.jackson.dataobject.fixture.ITestBaseEntityDo;
+import org.eclipse.scout.rt.jackson.dataobject.fixture.OneTestItemContributionDo;
import org.eclipse.scout.rt.jackson.dataobject.fixture.TestBigIntegerDo;
import org.eclipse.scout.rt.jackson.dataobject.fixture.TestBinaryDo;
import org.eclipse.scout.rt.jackson.dataobject.fixture.TestBinaryResourceDo;
@@ -2090,6 +2091,19 @@
assertEquals("foo-id-1", marshalled.genericMapAttribute().get().get("foo-1").getId());
}
+ @Test
+ public void testSerializeDeserialize_DoEntityWithContributions() throws Exception {
+ TestItemDo doEntity = BEANS.get(TestItemDo.class).withId("123456789");
+ doEntity.contribution(OneTestItemContributionDo.class).withName("my");
+
+ String json = s_dataObjectMapper.writeValueAsString(doEntity);
+ assertJsonEquals("TestDoEntityWithContributions.json", json);
+
+ TestItemDo marshalledDoEntity = s_dataObjectMapper.readValue(json, TestItemDo.class);
+ assertEqualsWithComparisonFailure(doEntity, marshalledDoEntity);
+ assertTrue(doEntity.getNode(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME) instanceof DoCollection); // contributions node is always DoCollection
+ }
+
// ------------------------------------ entity with IDoEntity interface definition tests -----------------------------
@Test
diff --git a/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/fixture/OneTestItemContributionDo.java b/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/fixture/OneTestItemContributionDo.java
new file mode 100644
index 0000000..f806daf
--- /dev/null
+++ b/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/fixture/OneTestItemContributionDo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2018 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.rt.jackson.dataobject.fixture;
+
+import javax.annotation.Generated;
+
+import org.eclipse.scout.rt.dataobject.ContributesTo;
+import org.eclipse.scout.rt.dataobject.DoEntity;
+import org.eclipse.scout.rt.dataobject.DoValue;
+import org.eclipse.scout.rt.dataobject.IDoEntityContribution;
+import org.eclipse.scout.rt.dataobject.TypeName;
+
+@TypeName("OneTestItemContribution")
+@ContributesTo(TestItemDo.class)
+public class OneTestItemContributionDo extends DoEntity implements IDoEntityContribution {
+
+ public DoValue<String> name() {
+ return doValue("name");
+ }
+
+ /* **************************************************************************
+ * GENERATED CONVENIENCE METHODS
+ * *************************************************************************/
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public OneTestItemContributionDo withName(String name) {
+ name().set(name);
+ return this;
+ }
+
+ @Generated("DoConvenienceMethodsGenerator")
+ public String getName() {
+ return name().get();
+ }
+}
diff --git a/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/fixture/TestCustomImplementedEntityDo.java b/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/fixture/TestCustomImplementedEntityDo.java
index eff8359..5e14a5f 100644
--- a/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/fixture/TestCustomImplementedEntityDo.java
+++ b/org.eclipse.scout.rt.jackson.test/src/test/java/org/eclipse/scout/rt/jackson/dataobject/fixture/TestCustomImplementedEntityDo.java
@@ -30,6 +30,7 @@
import org.eclipse.scout.rt.dataobject.DoSet;
import org.eclipse.scout.rt.dataobject.DoValue;
import org.eclipse.scout.rt.dataobject.IDoEntity;
+import org.eclipse.scout.rt.dataobject.IDoEntityContribution;
import org.eclipse.scout.rt.dataobject.IValueFormatConstants;
import org.eclipse.scout.rt.dataobject.TypeName;
import org.eclipse.scout.rt.dataobject.ValueFormat;
@@ -120,6 +121,36 @@
}
@Override
+ public Collection<IDoEntityContribution> getContributions() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <CONTRIBUTION extends IDoEntityContribution> CONTRIBUTION getContribution(Class<CONTRIBUTION> contributionClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <CONTRIBUTION extends IDoEntityContribution> void putContribution(CONTRIBUTION contribution) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <CONTRIBUTION extends IDoEntityContribution> CONTRIBUTION contribution(Class<CONTRIBUTION> contributionClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasContribution(Class<? extends IDoEntityContribution> contributionClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeContribution(Class<? extends IDoEntityContribution> contributionClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean equals(Object obj) {
return ObjectUtility.equals(m_attributes, ((TestCustomImplementedEntityDo) obj).m_attributes);
}
diff --git a/org.eclipse.scout.rt.jackson.test/src/test/resources/org/eclipse/scout/rt/jackson/dataobject/TestDoEntityWithContributions.json b/org.eclipse.scout.rt.jackson.test/src/test/resources/org/eclipse/scout/rt/jackson/dataobject/TestDoEntityWithContributions.json
new file mode 100644
index 0000000..b04b779
--- /dev/null
+++ b/org.eclipse.scout.rt.jackson.test/src/test/resources/org/eclipse/scout/rt/jackson/dataobject/TestDoEntityWithContributions.json
@@ -0,0 +1,8 @@
+{
+ "_type" : "TestItem",
+ "_contributions" : [ {
+ "_type" : "OneTestItemContribution",
+ "name" : "my"
+ } ],
+ "id" : "123456789"
+}
diff --git a/org.eclipse.scout.rt.jackson/src/main/java/org/eclipse/scout/rt/jackson/dataobject/DoEntityDeserializer.java b/org.eclipse.scout.rt.jackson/src/main/java/org/eclipse/scout/rt/jackson/dataobject/DoEntityDeserializer.java
index 578e194..01daefc 100644
--- a/org.eclipse.scout.rt.jackson/src/main/java/org/eclipse/scout/rt/jackson/dataobject/DoEntityDeserializer.java
+++ b/org.eclipse.scout.rt.jackson/src/main/java/org/eclipse/scout/rt/jackson/dataobject/DoEntityDeserializer.java
@@ -195,10 +195,10 @@
protected JavaType findResolvedAttributeType(IDoEntity entityInstance, String attributeName, boolean isObject, boolean isArray) {
return m_doEntityDeserializerTypeResolver.resolveAttributeType(entityInstance.getClass(), attributeName)
- .orElseGet(() -> findResolvedFallbackAttributeType(isObject, isArray));
+ .orElseGet(() -> findResolvedFallbackAttributeType(attributeName, isObject, isArray));
}
- protected JavaType findResolvedFallbackAttributeType(boolean isObject, boolean isArray) {
+ protected JavaType findResolvedFallbackAttributeType(String attributeName, boolean isObject, boolean isArray) {
if (DoMapEntity.class.isAssignableFrom(m_handledClass)) {
// DoMapEntity<T> structure is deserialized as typed Map<String, T>
return findResolvedDoMapEntityType();
@@ -209,7 +209,8 @@
}
else if (isArray) {
// array-like JSON structure is deserialized as raw DoList (using DoList as generic structure instead of DoSet or DoCollection)
- return TypeFactory.defaultInstance().constructType(DoList.class);
+ // Exception: internal node _contributions is deserialized using a DoCollection
+ return TypeFactory.defaultInstance().constructType(DoEntity.CONTRIBUTIONS_ATTRIBUTE_NAME.equals(attributeName) ? DoCollection.class : DoList.class);
}
else {
// JSON scalar values are deserialized as raw object using default jackson typing