You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2013/06/06 11:33:01 UTC
[1/2] git commit: ISIS-422: extending IsisJdoSupport to provide
acccess to JDO PersistenceManager.
Updated Branches:
refs/heads/master bd39f6806 -> c375a4f1d
ISIS-422: extending IsisJdoSupport to provide acccess to JDO PersistenceManager.
Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/53722484
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/53722484
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/53722484
Branch: refs/heads/master
Commit: 5372248448abc112bf0711667f887ccb2bfd7b59
Parents: bd39f68
Author: Dan Haywood <da...@apache.org>
Authored: Thu Jun 6 09:44:05 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Thu Jun 6 09:44:05 2013 +0100
----------------------------------------------------------------------
.../jdo/applib/service/support/IsisJdoSupport.java | 4 ++++
.../service/support/IsisJdoSupportImpl.java | 13 ++++++++++++-
2 files changed, 16 insertions(+), 1 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/isis/blob/53722484/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/support/IsisJdoSupport.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/support/IsisJdoSupport.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/support/IsisJdoSupport.java
index b081f35..9cc54df 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/support/IsisJdoSupport.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/support/IsisJdoSupport.java
@@ -19,6 +19,8 @@
package org.apache.isis.objectstore.jdo.applib.service.support;
+import javax.jdo.PersistenceManager;
+
import org.apache.isis.applib.annotation.Programmatic;
/**
@@ -43,4 +45,6 @@ public interface IsisJdoSupport {
*/
@Programmatic
<T> T refresh(T domainObject);
+
+ PersistenceManager getJdoPersistenceManager();
}
http://git-wip-us.apache.org/repos/asf/isis/blob/53722484/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java
index 647bd4e..d2a0a91 100644
--- a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java
+++ b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/support/IsisJdoSupportImpl.java
@@ -19,6 +19,8 @@
package org.apache.isis.objectstore.jdo.datanucleus.service.support;
+import javax.jdo.PersistenceManager;
+
import org.apache.isis.applib.annotation.Hidden;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
@@ -33,12 +35,16 @@ public class IsisJdoSupportImpl implements IsisJdoSupport {
@Override
public <T> T refresh(T domainObject) {
- DataNucleusObjectStore objectStore = (DataNucleusObjectStore) getPersistenceSession().getObjectStore();
+ DataNucleusObjectStore objectStore = getObjectStore();
ObjectAdapter adapter = getAdapterManager().adapterFor(domainObject);
objectStore.refreshRoot(adapter);
return domainObject;
}
+ protected DataNucleusObjectStore getObjectStore() {
+ return (DataNucleusObjectStore) getPersistenceSession().getObjectStore();
+ }
+
protected AdapterManager getAdapterManager() {
return getPersistenceSession().getAdapterManager();
}
@@ -51,4 +57,9 @@ public class IsisJdoSupportImpl implements IsisJdoSupport {
return getPersistenceSession().getServicesInjector();
}
+ @Override
+ public PersistenceManager getJdoPersistenceManager() {
+ return getObjectStore().getPersistenceManager();
+ }
+
}
[2/2] git commit: ISIS-423: utility to automatically test bidir
relationships.
Posted by da...@apache.org.
ISIS-423: utility to automatically test bidir relationships.
Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/c375a4f1
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/c375a4f1
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/c375a4f1
Branch: refs/heads/master
Commit: c375a4f1db84113b4d089e038389b97e2ae6803e
Parents: 5372248
Author: Dan Haywood <da...@apache.org>
Authored: Thu Jun 6 09:44:31 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Thu Jun 6 09:44:31 2013 +0100
----------------------------------------------------------------------
core/unittestsupport/pom.xml | 12 +
...irectionalRelationshipContractTestAbstract.java | 789 +++++++++++++++
.../isis/core/unittestsupport/bidir/Child.java | 52 +
.../core/unittestsupport/bidir/CollectUtils.java | 29 +
.../core/unittestsupport/bidir/IndentPrinter.java | 177 ++++
.../core/unittestsupport/bidir/Instantiator.java | 29 +
.../unittestsupport/bidir/InstantiatorMap.java | 55 +
.../unittestsupport/bidir/InstantiatorSimple.java | 35 +
.../core/unittestsupport/bidir/Instantiators.java | 24 +
.../isis/core/unittestsupport/bidir/Parent.java | 91 ++
.../core/unittestsupport/bidir/ReflectUtils.java | 81 ++
.../core/unittestsupport/bidir/StringUtils.java | 37 +
.../BidirectionalRelationshipContractTestAll.java | 18 +
.../unittestsupport/bidir/ChildDomainObject.java | 76 ++
.../bidir/InstantiatorForChildDomainObject.java | 36 +
.../unittestsupport/bidir/ParentDomainObject.java | 65 ++
.../unittestsupport/bidir/PeerDomainObject.java | 103 ++
.../bidir/PeerDomainObjectForTesting.java | 23 +
.../main/java/objstore/jdo/todo/ToDoItemsJdo.java | 1 -
19 files changed, 1732 insertions(+), 1 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/pom.xml
----------------------------------------------------------------------
diff --git a/core/unittestsupport/pom.xml b/core/unittestsupport/pom.xml
index 70cd28f..b329cb4 100644
--- a/core/unittestsupport/pom.xml
+++ b/core/unittestsupport/pom.xml
@@ -93,6 +93,18 @@
<artifactId>jmock-legacy</artifactId>
</dependency>
+ <dependency>
+ <groupId>javax.jdo</groupId>
+ <artifactId>jdo-api</artifactId>
+ <version>3.0.1</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.reflections</groupId>
+ <artifactId>reflections</artifactId>
+ </dependency>
+
</dependencies>
</project>
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAbstract.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAbstract.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAbstract.java
new file mode 100644
index 0000000..2d7d92c
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAbstract.java
@@ -0,0 +1,789 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Set;
+
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Persistent;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import com.google.common.io.ByteStreams;
+
+import org.hamcrest.Matchers;
+import org.junit.Test;
+import org.reflections.ReflectionUtils;
+import org.reflections.Reflections;
+
+public abstract class BidirectionalRelationshipContractTestAbstract implements Instantiators {
+
+ private final Reflections reflections;
+ private final InstantiatorMap instantiatorMap;
+ private IndentPrinter out;
+
+ protected BidirectionalRelationshipContractTestAbstract(
+ final String packagePrefix,
+ ImmutableMap<Class<?>,Instantiator> instantiatorsByClass) {
+ reflections = new Reflections(packagePrefix);
+ instantiatorMap = new InstantiatorMap(instantiatorsByClass);
+ out = new IndentPrinter(new PrintWriter(ByteStreams.nullOutputStream()));
+ }
+
+ public Instantiators withLoggingTo(Writer out) {
+ this.out = new IndentPrinter(out);
+ return this;
+ }
+
+ public Instantiators withLoggingTo(PrintStream out) {
+ this.out = new IndentPrinter(new PrintWriter(out));
+ return this;
+ }
+
+ @Test
+ public void searchAndTest() throws Exception {
+
+ Set<Class<?>> entityTypes =
+ Sets.newTreeSet(new Comparator<Class<?>>() {
+
+ @Override
+ public int compare(Class<?> o1, Class<?> o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ entityTypes.addAll(reflections.getTypesAnnotatedWith(PersistenceCapable.class));
+
+ for (Class<?> entityType : entityTypes) {
+ out.println(entityType.getName());
+ out.incrementIndent();
+ try {
+ process(entityType);
+ } finally {
+ out.decrementIndent();
+ }
+ }
+ out.println("DONE");
+ out.flush();
+ }
+
+ private void process(Class<?> entityType) {
+ final Set<Field> mappedByFields = Reflections.getAllFields(entityType, ReflectUtils.persistentMappedBy);
+ for (Field mappedByField : mappedByFields) {
+ final Parent p = new Parent();
+ p.entityType = entityType;
+ p.childField = mappedByField;
+ try {
+ out.println("processing " + p.entityType.getSimpleName() + "#" + p.childField.getName());
+ out.incrementIndent();
+ process(p);
+ } finally {
+ out.decrementIndent();
+ }
+ }
+ }
+
+ private void process(Parent p) {
+
+ // mappedBy
+ final Persistent persistentAnnotation = p.childField.getAnnotation(Persistent.class);
+ p.mappedBy = persistentAnnotation.mappedBy();
+
+ // getMethod
+ final String getMethod = StringUtils.methodNamed("get", p.childField);
+ final Set<Method> getMethods = Reflections.getAllMethods(p.entityType, ReflectionUtils.withName(getMethod));
+ assertThat(p.desc() + ": no getXxx() method", getMethods.size(), is(1));
+ p.getMethod = CollectUtils.firstIn(getMethods);
+
+ final Child c = new Child();
+
+ final Class<?> returnType = p.getMethod.getReturnType();
+ if(Collection.class.isAssignableFrom(returnType)) {
+ // addToMethod
+ final String addToMethod = StringUtils.methodNamed("addTo", p.childField);
+ final Set<Method> addToMethods = Reflections.getAllMethods(p.entityType,
+ Predicates.and(ReflectionUtils.withName(addToMethod), ReflectionUtils.withParametersCount(1), ReflectUtils.withEntityParameter()));
+ if(addToMethods.size() != 1) {
+ // just skip
+ out.println("no addToXxx() method in parent");
+ return;
+ }
+ p.addToMethod = CollectUtils.firstIn(addToMethods);
+
+ // removeFromMethod
+ final String removeFromMethod = StringUtils.methodNamed("removeFrom", p.childField);
+ final Set<Method> removeFromMethods = Reflections.getAllMethods(p.entityType,
+ Predicates.and(ReflectionUtils.withName(removeFromMethod), ReflectionUtils.withParametersCount(1), ReflectUtils.withEntityParameter()));
+ if(removeFromMethods.size() != 1) {
+ // just skip
+ out.println("no removeFromXxx() method in parent");
+ return;
+ }
+ p.removeFromMethod = CollectUtils.firstIn(removeFromMethods);
+
+ // child's entityType
+ final Class<?> addToParameterType = p.addToMethod.getParameterTypes()[0];
+ final Class<?> removeFromParameterType = p.removeFromMethod.getParameterTypes()[0];
+
+ assertThat(p.desc() + ": " + p.addToMethod.getName() + " and " + p.removeFromMethod.getName() + " should have the same parameter type",
+ addToParameterType == removeFromParameterType, is(true));
+
+ c.entityType = addToParameterType;
+ } else {
+
+ // modify
+ String modifyMethod = StringUtils.methodNamed("modify", p.childField);
+ final Set<Method> modifyMethods = Reflections.getAllMethods(p.entityType,
+ Predicates.and(Reflections.withName(modifyMethod), ReflectionUtils.withParametersCount(1), ReflectUtils.withEntityParameter()));
+ if(modifyMethods.size() != 1) {
+ // just skip
+ out.println("no modifyXxx() method in parent");
+ return;
+ }
+ p.modifyMethod = CollectUtils.firstIn(modifyMethods);
+
+ // clear
+ String clearMethod = StringUtils.methodNamed("clear", p.childField);
+ final Set<Method> clearMethods = Reflections.getAllMethods(p.entityType,
+ Predicates.and(Reflections.withName(clearMethod), ReflectionUtils.withParametersCount(0)));
+ if(clearMethods.size() != 1) {
+ // just skip
+ out.println("no clearXxx() method in parent");
+ return;
+ }
+ p.clearMethod = CollectUtils.firstIn(clearMethods);
+
+ // child's entityType
+ c.entityType = p.modifyMethod.getParameterTypes()[0];
+ }
+
+ final Instantiator parentInstantiator = instantiatorFor(p.entityType);
+ if(parentInstantiator == null) {
+ out.println("no instantiator for " + p.entityType.getName());
+ // just skip
+ return;
+ }
+ final Instantiator childInstantiator = instantiatorFor(c.entityType);
+ if(childInstantiator == null) {
+ out.println("no instantiator for " + c.entityType.getName());
+ // just skip
+ return;
+ }
+
+ process(p, c);
+ }
+
+ private Instantiator instantiatorFor(final Class<?> cls) {
+ Instantiator instantiator = instantiatorMap.get(cls);
+ if(instantiator != null) {
+ return instantiator;
+ }
+
+ instantiator = doInstantiatorFor(cls);
+
+ instantiator = instantiatorMap.put(cls, instantiator);
+ return instantiator != Instantiator.NOOP? instantiator: null;
+ }
+
+
+ /**
+ * Default just tries to use the {@link InstantiatorSimple};
+ * subclasses can override with more sophisticated implementations if required.
+ */
+ protected Instantiator doInstantiatorFor(final Class<?> cls) {
+ return new InstantiatorSimple(cls);
+ }
+
+ private void process(Parent p, Child c) {
+
+ // mappedBy field
+ final Set<Field> parentFields = Reflections.getAllFields(c.entityType, Predicates.and(Reflections.withName(p.mappedBy), ReflectUtils.withTypeAssignableFrom(p.entityType)));
+
+ assertThat(c.entityType.getName()+ ": could not locate '" + p.mappedBy + "' field, returning supertype of " + p.entityType.getSimpleName() +", (as per @Persistent(mappedBy=...) in parent "+ p.entityType.getSimpleName()+")", parentFields.size(), is(1));
+ c.parentField = CollectUtils.firstIn(parentFields);
+
+ // getter
+ String getterMethod = StringUtils.methodNamed("get", c.parentField);
+ final Set<Method> getterMethods = Reflections.getAllMethods(c.entityType,
+ Predicates.and(Reflections.withName(getterMethod), ReflectionUtils.withParametersCount(0), ReflectUtils.withReturnTypeAssignableFrom(p.entityType)));
+ assertThat(p.descRel(c) +": could not locate getter " + getterMethod + "() returning supertype of " + p.entityType.getSimpleName(), getterMethods.size(), is(1));
+ c.getMethod = CollectUtils.firstIn(getterMethods);
+
+ // modify
+ String modifyMethod = StringUtils.methodNamed("modify", c.parentField);
+ final Set<Method> modifyMethods = Reflections.getAllMethods(c.entityType,
+ Predicates.and(Reflections.withName(modifyMethod), ReflectionUtils.withParametersCount(1), ReflectUtils.withParametersAssignableFrom(p.entityType)));
+ if(modifyMethods.size() != 1) {
+ // just skip
+ out.println("no modifyXxx() method in child");
+ return;
+ }
+ c.modifyMethod = CollectUtils.firstIn(modifyMethods);
+
+ // clear
+ String clearMethod = StringUtils.methodNamed("clear", c.parentField);
+ final Set<Method> clearMethods = Reflections.getAllMethods(c.entityType,
+ Predicates.and(Reflections.withName(clearMethod), ReflectionUtils.withParametersCount(0)));
+ if(clearMethods.size() != 1) {
+ // just skip
+ out.println("no clearXxx() method in child");
+ return;
+ }
+ c.clearMethod = CollectUtils.firstIn(clearMethods);
+
+ exercise(p, c);
+ }
+
+ @Override
+ public Object newInstance(final Class<?> entityType) {
+ final Instantiator instantiator = instantiatorFor(entityType);
+ return instantiator.instantiate();
+ }
+
+ private static String assertDesc(Parent p, Child c, String methodDesc, String testDesc) {
+ return p.descRel(c) +": " + methodDesc + ": " + testDesc;
+ }
+
+ private void exercise(Parent p, Child c) {
+ out.println("exercising " + p.descRel(c));
+ out.incrementIndent();
+ try {
+ if(p.addToMethod != null) {
+ // 1:m
+
+ // add
+ oneToManyParentAddTo(p, c);
+ oneToManyParentAddToWhenAlreadyChild(p, c);
+ oneToManyParentAddToWhenNull(p, c);
+ oneToManyChildModify(p, c);
+ oneToManyChildModifyWhenAlreadyParent(p, c);
+ oneToManyChildModifyWhenNull(p, c);
+
+ // move (update)
+ oneToManyChildModifyToNewParent(p, c);
+ oneToManyChildModifyToExistingParent(p, c);
+
+ // delete
+ oneToManyParentRemoveFrom(p, c);
+ oneToManyParentRemoveFromWhenNotAssociated(p, c);
+ oneToManyParentRemoveFromWhenNull(p, c);
+ oneToManyChildClear(p, c);
+ oneToManyChildClearWhenNotAssociated(p, c);
+ } else {
+ // 1:1
+
+ // add
+ oneToOneParentModify(p, c);
+ oneToOneParentModifyWhenAlreadyChild(p, c);
+ oneToOneParentModifyWhenNull(p, c);
+ oneToOneChildModify(p, c);
+ oneToOneChildModifyWhenAlreadyParent(p, c);
+ oneToOneChildModifyWhenNull(p, c);
+
+ // move (update)
+ oneToOneChildModifyToNewParent(p, c);
+ oneToOneChildModifyToExistingParent(p, c);
+
+ // delete
+ oneToOneParentClear(p, c);
+ oneToOneChildClear(p, c);
+ oneToOneChildClearWhenNotAssociated(p, c);
+ }
+
+ } finally {
+ out.decrementIndent();
+ }
+
+ }
+
+ ////////////////
+ // 1:m
+ ////////////////
+
+ private void oneToManyParentAddTo(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyParentAddTo";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ // when
+ p.addToChildren(parent1, child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent contains child"), p.getChildren(parent1), Matchers.containsInAnyOrder(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child references parent"), c.getParent(child1), is(parent1));
+ }
+
+ private void oneToManyParentAddToWhenAlreadyChild(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyParentAddToWhenAlreadyChild";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ // given
+ p.addToChildren(parent1, child1);
+
+ // when
+ p.addToChildren(parent1, child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still contains child"), p.getChildren(parent1), Matchers.containsInAnyOrder(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child still references parent"), c.getParent(child1), is(parent1));
+ }
+
+ private void oneToManyParentAddToWhenNull(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyParentAddToWhenNull";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+
+ // when
+ p.addToChildren(parent1, null);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent does not have any children"), p.getChildren(parent1).isEmpty(), is(true));
+ }
+
+ private void oneToManyChildModify(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyChildModify";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ // when
+ c.modifyParent(child1, parent1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent contains child"), p.getChildren(parent1), Matchers.containsInAnyOrder(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child references parent"), c.getParent(child1), is(parent1));
+ }
+
+ private void oneToManyChildModifyWhenAlreadyParent(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyChildModifyWhenAlreadyParent";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ c.modifyParent(child1, parent1);
+
+ // when
+ c.modifyParent(child1, parent1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still contains child"), p.getChildren(parent1), Matchers.containsInAnyOrder(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child still references parent"), c.getParent(child1), is(parent1));
+ }
+
+
+ private void oneToManyChildModifyWhenNull(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyChildModifyWhenNull";
+ out.println(methodDesc);
+
+ // given
+ Object child1 = c.newChild(this);
+
+ // when
+ c.modifyParent(child1, null);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"child does not reference any parent"), c.getParent(child1), is(nullValue()));
+ }
+
+
+ private void oneToManyChildModifyToNewParent(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyChildModifyToNewParent";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object parent2 = p.newParent(this);
+ Object child1 = c.newChild(this);
+ Object child2 = c.newChild(this);
+
+ p.addToChildren(parent1, child1);
+ p.addToChildren(parent2, child2);
+
+ // when
+ c.modifyParent(child1, parent2);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent 1 no longer has any children"), p.getChildren(parent1).isEmpty(), is(true));
+ assertThat(assertDesc(p,c,methodDesc,"parent 2 now has both children"), p.getChildren(parent2), Matchers.containsInAnyOrder(child1, child2));
+ assertThat(assertDesc(p,c,methodDesc,"child 1 now references parent 2"), c.getParent(child1), is(parent2));
+ assertThat(assertDesc(p,c,methodDesc,"child 2 still references parent 2"), c.getParent(child2), is(parent2));
+ }
+
+ private void oneToManyChildModifyToExistingParent(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyChildModifyToExistingParent";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object parent2 = p.newParent(this);
+ Object child1 = c.newChild(this);
+ Object child2 = c.newChild(this);
+
+ p.addToChildren(parent1, child1);
+ p.addToChildren(parent2, child2);
+
+ // when
+ c.modifyParent(child1, parent1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent 1 still contains child 1"), p.getChildren(parent1), Matchers.containsInAnyOrder(child1));
+ assertThat(assertDesc(p,c,methodDesc,"parent 2 still contains child 2"), p.getChildren(parent2), Matchers.containsInAnyOrder(child2));
+ assertThat(assertDesc(p,c,methodDesc,"child 1 still references parent 1"), c.getParent(child1), is(parent1));
+ assertThat(assertDesc(p,c,methodDesc,"child 2 still references parent 2"), c.getParent(child2), is(parent2));
+ }
+
+ private void oneToManyParentRemoveFrom(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyParentRemoveFrom";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ p.addToChildren(parent1, child1);
+
+ // when
+ p.removeFromChildren(parent1, child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent no longer contains child"), p.getChildren(parent1).isEmpty(), is(true));
+ assertThat(assertDesc(p,c,methodDesc,"child no longer references parent"), c.getParent(child1), is(nullValue()));
+ }
+
+ private void oneToManyParentRemoveFromWhenNull(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyParentRemoveFromWhenNull";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ p.addToChildren(parent1, child1);
+
+ // when
+ p.removeFromChildren(parent1, null);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still contains child"), p.getChildren(parent1), Matchers.containsInAnyOrder(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child still references parent"), c.getParent(child1), is(parent1));
+ }
+
+ private void oneToManyParentRemoveFromWhenNotAssociated(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyParentRemoveFromWhenNotAssociated";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+ Object child2 = c.newChild(this);
+
+ p.addToChildren(parent1, child1);
+
+ // when
+ p.removeFromChildren(parent1, child2);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still contains child"), p.getChildren(parent1), Matchers.containsInAnyOrder(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child still references parent"), c.getParent(child1), is(parent1));
+ }
+
+ private void oneToManyChildClear(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyChildClear";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ p.addToChildren(parent1, child1);
+
+ // when
+ c.clearParent(child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent no longer contains child"), p.getChildren(parent1).isEmpty(), is(true));
+ assertThat(assertDesc(p,c,methodDesc,"child no longer references parent"), c.getParent(child1), is(nullValue()));
+ }
+
+ private void oneToManyChildClearWhenNotAssociated(Parent p, Child c) {
+
+ final String methodDesc = "oneToManyChildClearWhenNotAssociated";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ // when
+ c.clearParent(child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still does not reference child"), p.getChildren(parent1).isEmpty(), is(true));
+ assertThat(assertDesc(p,c,methodDesc,"child still does not reference parent"), c.getParent(child1), is(nullValue()));
+ }
+
+
+
+ ////////////////
+ // 1:1
+ ////////////////
+
+ private void oneToOneParentModify(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneParentModify";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ // when
+ p.modifyChild(parent1, child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent references child"),p.getChild(parent1), is(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child references parent"), c.getParent(child1), is(parent1));
+ }
+
+ private void oneToOneParentModifyWhenAlreadyChild(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneParentModifyWhenAlreadyChild";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ p.modifyChild(parent1, child1);
+
+ // when
+ p.modifyChild(parent1, child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still references child"), p.getChild(parent1), is(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child still references parent"), c.getParent(child1), is(parent1));
+ }
+
+ private void oneToOneParentModifyWhenNull(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneParentModifyWhenNull";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+
+ // when
+ p.modifyChild(parent1, null);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still references child"), p.getChild(parent1), is(nullValue()));
+ }
+
+ private void oneToOneChildModify(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneChildModify";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ // when
+ c.modifyParent(child1, parent1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent references child"), p.getChild(parent1), is(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child references parent"), c.getParent(child1), is(parent1));
+ }
+
+ private void oneToOneChildModifyWhenAlreadyParent(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneChildModifyWhenAlreadyParent";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ c.modifyParent(child1, parent1);
+
+ // when
+ c.modifyParent(child1, parent1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still references child"), p.getChild(parent1), is(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child still references parent"), c.getParent(child1), is(parent1));
+ }
+
+
+ private void oneToOneChildModifyWhenNull(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneChildModifyWhenNull";
+ out.println(methodDesc);
+
+ // given
+ Object child1 = c.newChild(this);
+
+ // when
+ c.modifyParent(child1, null);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"child still has no parent"), c.getParent(child1), is(nullValue()));
+ }
+
+
+ private void oneToOneChildModifyToNewParent(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneChildModifyToNewParent";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object parent2 = p.newParent(this);
+ Object child1 = c.newChild(this);
+ Object child2 = c.newChild(this);
+
+ p.modifyChild(parent1, child1);
+ p.modifyChild(parent2, child2);
+
+ // when
+ c.modifyParent(child1, parent2);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent 1 no longer references child 1"), p.getChild(parent1), is(nullValue()));
+ assertThat(assertDesc(p,c,methodDesc,"parent 2 now references child 1"), p.getChild(parent2), is(child1));
+ assertThat(assertDesc(p,c,methodDesc,"child 1 now references parent 2"), c.getParent(child1), is(parent2));
+ assertThat(assertDesc(p,c,methodDesc,"child 2, as a side-effect, no longer references parent 2"), c.getParent(child2), is(nullValue()));
+ }
+
+ private void oneToOneChildModifyToExistingParent(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneChildModifyToExistingParent";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object parent2 = p.newParent(this);
+ Object child1 = c.newChild(this);
+ Object child2 = c.newChild(this);
+
+ p.modifyChild(parent1, child1);
+ p.modifyChild(parent2, child2);
+
+ // when
+ c.modifyParent(child1, parent1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent 1 still references child 1"), p.getChild(parent1), is(child1));
+ assertThat(assertDesc(p,c,methodDesc,"parent 2 still references child 2"), p.getChild(parent2), is(child2));
+ assertThat(assertDesc(p,c,methodDesc,"child 1 still references parent 1"), c.getParent(child1), is(parent1));
+ assertThat(assertDesc(p,c,methodDesc,"child 2 still references parent 2"), c.getParent(child2), is(parent2));
+ }
+
+ private void oneToOneParentClear(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneParentClear";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ p.modifyChild(parent1, child1);
+
+ // when
+ p.clearChild(parent1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent no longer references child"), p.getChild(parent1), is(nullValue()));
+ assertThat(assertDesc(p,c,methodDesc,"child no longer references parent"), c.getParent(child1), is(nullValue()));
+ }
+
+
+ private void oneToOneChildClear(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneChildClear";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ p.modifyChild(parent1, child1);
+
+ // when
+ c.clearParent(child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent no longer references child"), p.getChild(parent1), is(nullValue()));
+ assertThat(assertDesc(p,c,methodDesc,"child no longer references parent"), c.getParent(child1), is(nullValue()));
+ }
+
+ private void oneToOneChildClearWhenNotAssociated(Parent p, Child c) {
+
+ final String methodDesc = "oneToOneChildClearWhenNotAssociated";
+ out.println(methodDesc);
+
+ // given
+ Object parent1 = p.newParent(this);
+ Object child1 = c.newChild(this);
+
+ // when
+ c.clearParent(child1);
+
+ // then
+ assertThat(assertDesc(p,c,methodDesc,"parent still does not reference child"), p.getChild(parent1), is(nullValue()));
+ assertThat(assertDesc(p,c,methodDesc,"child still does not reference parent"), c.getParent(child1), is(nullValue()));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Child.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Child.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Child.java
new file mode 100644
index 0000000..4585bed
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Child.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+class Child {
+ Class<?> entityType;
+ Field parentField;
+ Method getMethod;
+ Method modifyMethod;
+ Method clearMethod;
+ Object getParent(Object child) {
+ try {
+ return getMethod.invoke(child);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void modifyParent(Object child, Object parent) {
+ try {
+ modifyMethod.invoke(child, parent);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void clearParent(Object child) {
+ try {
+ clearMethod.invoke(child);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ Object newChild(Instantiators instantiators) {
+ return instantiators.newInstance(entityType);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/CollectUtils.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/CollectUtils.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/CollectUtils.java
new file mode 100644
index 0000000..d23003b
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/CollectUtils.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.lang.reflect.Field;
+import java.util.Set;
+
+class CollectUtils {
+
+ static <T> T firstIn(final Set<T> set) {
+ return set.iterator().next();
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/IndentPrinter.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/IndentPrinter.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/IndentPrinter.java
new file mode 100644
index 0000000..aa10637
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/IndentPrinter.java
@@ -0,0 +1,177 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Adapted from <tt>groovy.util.IndentPrinter</tt> (published under ASL 2.0).
+ */
+class IndentPrinter {
+
+ private int indentLevel;
+ private String indent;
+ private Writer out;
+ private final boolean addNewlines;
+
+ /**
+ * Creates an IndentPrinter backed by a PrintWriter pointing to System.out, with an indent of two spaces.
+ *
+ * @see #IndentPrinter(Writer, String)
+ */
+ public IndentPrinter() {
+ this(new PrintWriter(System.out), " ");
+ }
+
+ /**
+ * Creates an IndentPrinter backed by the supplied Writer, with an indent of two spaces.
+ *
+ * @param out Writer to output to
+ * @see #IndentPrinter(Writer, String)
+ */
+ public IndentPrinter(Writer out) {
+ this(out, " ");
+ }
+
+ /**
+ * Creates an IndentPrinter backed by the supplied Writer,
+ * with a user-supplied String to be used for indenting.
+ *
+ * @param out Writer to output to
+ * @param indent character(s) used to indent each line
+ */
+ public IndentPrinter(Writer out, String indent) {
+ this(out, indent, true);
+ }
+
+ /**
+ * Creates an IndentPrinter backed by the supplied Writer,
+ * with a user-supplied String to be used for indenting
+ * and the ability to override newline handling.
+ *
+ * @param out Writer to output to
+ * @param indent character(s) used to indent each line
+ * @param addNewlines set to false to gobble all new lines (default true)
+ */
+ public IndentPrinter(Writer out, String indent, boolean addNewlines) {
+ this.addNewlines = addNewlines;
+ if (out == null) {
+ throw new IllegalArgumentException("Must specify a Writer");
+ }
+ this.out = out;
+ this.indent = indent;
+ }
+
+ /**
+ * Prints a string followed by an end of line character.
+ *
+ * @param text String to be written
+ */
+ public void println(String text) {
+ printIndent();
+ try {
+ out.write(text);
+ println();
+ flush();
+ } catch(IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ /**
+ * Prints a string.
+ *
+ * @param text String to be written
+ */
+ public void print(String text) {
+ try {
+ out.write(text);
+ } catch(IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ /**
+ * Prints a character.
+ *
+ * @param c char to be written
+ */
+ public void print(char c) {
+ try {
+ out.write(c);
+ } catch(IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ /**
+ * Prints the current indent level.
+ */
+ public void printIndent() {
+ for (int i = 0; i < indentLevel; i++) {
+ try {
+ out.write(indent);
+ } catch(IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+ }
+
+ /**
+ * Prints an end-of-line character (if enabled via addNewLines property).
+ * Defaults to outputting a single '\n' character but by using a custom
+ * Writer, e.g. PlatformLineWriter, you can get platform-specific
+ * end-of-line characters.
+ *
+ * @see #IndentPrinter(Writer, String, boolean)
+ */
+ public void println() {
+ if (addNewlines) {
+ try {
+ out.write("\n");
+ } catch(IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+ }
+
+ public void incrementIndent() {
+ ++indentLevel;
+ }
+
+ public void decrementIndent() {
+ --indentLevel;
+ }
+
+ public int getIndentLevel() {
+ return indentLevel;
+ }
+
+ public void setIndentLevel(int indentLevel) {
+ this.indentLevel = indentLevel;
+ }
+
+ public void flush() {
+ try {
+ out.flush();
+ } catch(IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Instantiator.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Instantiator.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Instantiator.java
new file mode 100644
index 0000000..9678dbc
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Instantiator.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+public interface Instantiator {
+ Instantiator NOOP = new Instantiator() {
+
+ @Override
+ public Object instantiate() {
+ return null;
+ }
+ };
+
+ public Object instantiate();
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorMap.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorMap.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorMap.java
new file mode 100644
index 0000000..b4e661c
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorMap.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+class InstantiatorMap {
+
+ private Map<Class<?>, Instantiator> instantiatorMap;
+
+ public InstantiatorMap(ImmutableMap<Class<?>, Instantiator> instantiatorMap) {
+ this.instantiatorMap = Maps.newHashMap(instantiatorMap);
+ }
+
+ Instantiator get(Class<?> cls) {
+ return instantiatorMap.get(cls);
+ }
+
+ Instantiator put(Class<?> cls, Instantiator instantiator) {
+
+ if(instantiator != null) {
+ // check it works instantiator
+ try {
+ @SuppressWarnings("unused")
+ final Object dummy = instantiator.instantiate();
+ } catch(RuntimeException ex) {
+ instantiator = Instantiator.NOOP;
+ }
+ } else {
+ instantiator = Instantiator.NOOP;
+ }
+
+ instantiatorMap.put(cls, instantiator);
+
+ return instantiator;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorSimple.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorSimple.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorSimple.java
new file mode 100644
index 0000000..8ae8267
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorSimple.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+public class InstantiatorSimple implements Instantiator {
+
+ public final Class<?> cls;
+
+ public InstantiatorSimple(Class<?> cls) {
+ this.cls = cls;
+ }
+
+ @Override
+ public Object instantiate() {
+ try {
+ return cls.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Instantiators.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Instantiators.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Instantiators.java
new file mode 100644
index 0000000..d2ea382
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Instantiators.java
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+interface Instantiators {
+
+ public abstract Object newInstance(Class<?> entityType);
+
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Parent.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Parent.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Parent.java
new file mode 100644
index 0000000..fafb638
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/Parent.java
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+class Parent {
+ Class<?> entityType;
+ Field childField;
+ String mappedBy;
+ Method getMethod;
+
+ // for 1:m
+ Method addToMethod;
+ Method removeFromMethod;
+
+ // if 1:1
+ Method modifyMethod;
+ Method clearMethod;
+
+ Collection<?> getChildren(Object parent) throws RuntimeException {
+ try {
+ return (Collection<?>) getMethod.invoke(parent);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void addToChildren(Object parent, Object child) {
+ try {
+ addToMethod.invoke(parent, child);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void removeFromChildren(Object parent, Object child) {
+ try {
+ removeFromMethod.invoke(parent, child);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ Object getChild(Object parent) {
+ try {
+ return getMethod.invoke(parent);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void modifyChild(Object parent, Object child) {
+ try {
+ modifyMethod.invoke(parent, child);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void clearChild(Object parent) {
+ try {
+ clearMethod.invoke(parent);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ String desc() {
+ return entityType.getName()+"#"+childField.getName();
+ }
+ String descRel(Child c) {
+ final boolean oneToMany = addToMethod != null;
+ return entityType.getSimpleName() + "#" + childField.getName() + " 1:" + (oneToMany?"m":"1") + " "+ c.entityType.getSimpleName() + "#" + c.parentField.getName();
+ }
+
+ Object newParent(Instantiators instantiators) {
+ return instantiators.newInstance(entityType);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/ReflectUtils.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/ReflectUtils.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/ReflectUtils.java
new file mode 100644
index 0000000..dff7615
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/ReflectUtils.java
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Persistent;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
+
+class ReflectUtils {
+
+ public static <T> Predicate<Field> withTypeAssignableFrom(final Class<T> type) {
+ return new Predicate<Field>() {
+ public boolean apply(Field input) {
+ return input != null && input.getType().isAssignableFrom(type);
+ }
+ };
+ }
+
+ public static <T> Predicate<Method> withReturnTypeAssignableFrom(final Class<T> type) {
+ return new Predicate<Method>() {
+ public boolean apply(Method input) {
+ return input != null && input.getReturnType().isAssignableFrom(type);
+ }
+ };
+ }
+
+ public static Predicate<Method> withParametersAssignableFrom(final Class<?>... types) {
+ return new Predicate<Method>() {
+ public boolean apply(Method input) {
+ if (input != null) {
+ Class<?>[] parameterTypes = input.getParameterTypes();
+ if (parameterTypes.length == types.length) {
+ for (int i = 0; i < parameterTypes.length; i++) {
+ if (!parameterTypes[i].isAssignableFrom(types[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
+ static final Predicate<? super Field> persistentMappedBy = new Predicate<Field>() {
+ public boolean apply(Field f) {
+ final Persistent annotation = f.getAnnotation(Persistent.class);
+ return annotation!=null && !Strings.isNullOrEmpty(annotation.mappedBy());
+ }
+ };
+
+ static Predicate<? super Method> withEntityParameter() {
+ return new Predicate<Method>() {
+ public boolean apply(Method m) {
+ final Class<?> parameterType = m.getParameterTypes()[0];
+ return parameterType.isAnnotationPresent(PersistenceCapable.class);
+ }
+ };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/StringUtils.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/StringUtils.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/StringUtils.java
new file mode 100644
index 0000000..23e9b4a
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/bidir/StringUtils.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.lang.reflect.Field;
+
+class StringUtils {
+
+ public static String capitalize(final String str) {
+ if (str == null || str.length() == 0) {
+ return str;
+ }
+ if (str.length() == 1) {
+ return str.toUpperCase();
+ }
+ return Character.toUpperCase(str.charAt(0)) + str.substring(1);
+ }
+
+ public static String methodNamed(final String methodPrefix, final Field field) {
+ return methodPrefix + capitalize(field.getName());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAll.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAll.java b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAll.java
new file mode 100644
index 0000000..4e4c77c
--- /dev/null
+++ b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAll.java
@@ -0,0 +1,18 @@
+package org.apache.isis.core.unittestsupport.bidir;
+
+import com.google.common.collect.ImmutableMap;
+
+public class BidirectionalRelationshipContractTestAll extends BidirectionalRelationshipContractTestAbstract {
+
+ public BidirectionalRelationshipContractTestAll() {
+ super("org.apache.isis.core.unittestsupport.bidir",
+ ImmutableMap.<Class<?>,Instantiator>of(
+ // no instantiator need be registered for ParentDomainObject.class;
+ // will default to using new InstantiatorSimple(AgreementForTesting.class),
+ ChildDomainObject.class, new InstantiatorForChildDomainObject(),
+ PeerDomainObject.class, new InstantiatorSimple(PeerDomainObjectForTesting.class)
+ ));
+ withLoggingTo(System.out);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/ChildDomainObject.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/ChildDomainObject.java b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/ChildDomainObject.java
new file mode 100644
index 0000000..9d8ea06
--- /dev/null
+++ b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/ChildDomainObject.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import javax.jdo.annotations.PersistenceCapable;
+
+
+@PersistenceCapable
+public class ChildDomainObject implements Comparable<ChildDomainObject> {
+
+ // {{ Index (property)
+ private int index;
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(final int index) {
+ this.index = index;
+ }
+ // }}
+
+
+ // {{ Parent (property)
+ private ParentDomainObject parent;
+
+ public ParentDomainObject getParent() {
+ return parent;
+ }
+
+ public void setParent(final ParentDomainObject parent) {
+ this.parent = parent;
+ }
+ public void modifyParent(final ParentDomainObject parent) {
+ ParentDomainObject currentParent = getParent();
+ // check for no-op
+ if (parent == null || parent.equals(currentParent)) {
+ return;
+ }
+ // delegate to parent to associate
+ parent.addToChildren(this);
+ }
+
+ public void clearParent() {
+ ParentDomainObject currentParent = getParent();
+ // check for no-op
+ if (currentParent == null) {
+ return;
+ }
+ // delegate to parent to dissociate
+ currentParent.removeFromChildren(this);
+ }
+ // }}
+
+
+
+ @Override
+ public int compareTo(ChildDomainObject other) {
+ return this.getIndex() - other.getIndex();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorForChildDomainObject.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorForChildDomainObject.java b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorForChildDomainObject.java
new file mode 100644
index 0000000..a45a604
--- /dev/null
+++ b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorForChildDomainObject.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.util.SortedSet;
+
+/**
+ * To ensure that different ({@link Comparable}) {@link ChildDomainObject}s
+ * are not equivalent when placed into the {@link ParentDomainObject#getChildren() children} (a {@link SortedSet}.)
+ */
+public class InstantiatorForChildDomainObject implements Instantiator {
+
+ private int i;
+
+ @Override
+ public Object instantiate() {
+ final ChildDomainObject cdo = new ChildDomainObject();
+ cdo.setIndex(++i);
+ return cdo;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/ParentDomainObject.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/ParentDomainObject.java b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/ParentDomainObject.java
new file mode 100644
index 0000000..591f230
--- /dev/null
+++ b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/ParentDomainObject.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Persistent;
+
+@PersistenceCapable
+public class ParentDomainObject {
+
+ // {{ Children (Collection)
+ @Persistent(mappedBy="parent")
+ private SortedSet<ChildDomainObject> children = new TreeSet<ChildDomainObject>();
+
+ public SortedSet<ChildDomainObject> getChildren() {
+ return children;
+ }
+
+ public void setChildren(final SortedSet<ChildDomainObject> children) {
+ this.children = children;
+ }
+
+ public void addToChildren(final ChildDomainObject child) {
+ // check for no-op
+ if (child == null || getChildren().contains(child)) {
+ return;
+ }
+ // dissociate arg from its current parent (if any).
+ child.clearParent();
+ // associate arg
+ child.setParent(this);
+ getChildren().add(child);
+ }
+
+ public void removeFromChildren(final ChildDomainObject child) {
+ // check for no-op
+ if (child == null || !getChildren().contains(child)) {
+ return;
+ }
+ // dissociate arg
+ child.setParent(null);
+ getChildren().remove(child);
+ }
+
+ // }}
+
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObject.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObject.java b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObject.java
new file mode 100644
index 0000000..56f29e3
--- /dev/null
+++ b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObject.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Persistent;
+
+/**
+ * Not instantiable; the {@link PeerDomainObjectForTesting} must be used in the bidir contract testing instead.
+ */
+@PersistenceCapable
+public abstract class PeerDomainObject {
+
+ // {{ Next (property)
+ @Persistent(mappedBy="previous")
+ private PeerDomainObject next;
+
+ public PeerDomainObject getNext() {
+ return next;
+ }
+
+ public void setNext(final PeerDomainObject next) {
+ this.next = next;
+ }
+ public void modifyNext(final PeerDomainObject next) {
+ PeerDomainObject currentNext = getNext();
+ // check for no-op
+ if (next == null || next.equals(currentNext)) {
+ return;
+ }
+ // dissociate existing
+ clearNext();
+ // associate new
+ next.setPrevious(this);
+ setNext(next);
+ }
+
+ public void clearNext() {
+ PeerDomainObject currentNext = getNext();
+ // check for no-op
+ if (currentNext == null) {
+ return;
+ }
+ // dissociate existing
+ currentNext.setPrevious(null);
+ setNext(null);
+ }
+ // }}
+
+
+
+ // {{ Previous (property)
+ private PeerDomainObject previous;
+
+ public PeerDomainObject getPrevious() {
+ return previous;
+ }
+
+ public void setPrevious(final PeerDomainObject previous) {
+ this.previous = previous;
+ }
+
+ public void modifyPrevious(final PeerDomainObject previous) {
+ PeerDomainObject currentPrevious = getPrevious();
+ // check for no-op
+ if (previous == null || previous.equals(currentPrevious)) {
+ return;
+ }
+ // delegate to parent(s) to (re-)associate
+ if (currentPrevious != null) {
+ currentPrevious.clearNext();
+ }
+ previous.modifyNext(this);
+ }
+
+ public void clearPrevious() {
+ PeerDomainObject currentPrevious = getPrevious();
+ // check for no-op
+ if (currentPrevious == null) {
+ return;
+ }
+ // delegate to parent to dissociate
+ currentPrevious.clearNext();
+ }
+ // }}
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObjectForTesting.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObjectForTesting.java b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObjectForTesting.java
new file mode 100644
index 0000000..ed4fc70
--- /dev/null
+++ b/core/unittestsupport/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObjectForTesting.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import javax.jdo.annotations.Persistent;
+
+public class PeerDomainObjectForTesting extends PeerDomainObject {
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/c375a4f1/example/application/quickstart_wicket_restful_jdo/objstore-jdo/src/main/java/objstore/jdo/todo/ToDoItemsJdo.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/objstore-jdo/src/main/java/objstore/jdo/todo/ToDoItemsJdo.java b/example/application/quickstart_wicket_restful_jdo/objstore-jdo/src/main/java/objstore/jdo/todo/ToDoItemsJdo.java
index f5837f3..a16c228 100644
--- a/example/application/quickstart_wicket_restful_jdo/objstore-jdo/src/main/java/objstore/jdo/todo/ToDoItemsJdo.java
+++ b/example/application/quickstart_wicket_restful_jdo/objstore-jdo/src/main/java/objstore/jdo/todo/ToDoItemsJdo.java
@@ -18,7 +18,6 @@
*/
package objstore.jdo.todo;
-import java.util.Collections;
import java.util.List;
import com.google.common.base.Predicate;