001/**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.data.provider;
017
018import static org.junit.Assert.assertEquals;
019import static org.junit.Assert.assertFalse;
020import static org.junit.Assert.assertNotNull;
021import static org.junit.Assert.assertNull;
022import static org.junit.Assert.assertTrue;
023import static org.junit.Assert.fail;
024import static org.mockito.Matchers.any;
025import static org.mockito.Mockito.atLeastOnce;
026import static org.mockito.Mockito.never;
027import static org.mockito.Mockito.verify;
028import static org.mockito.Mockito.when;
029
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.Collection;
033import java.util.Collections;
034import java.util.List;
035import java.util.Map;
036
037import org.apache.commons.lang3.StringUtils;
038import org.junit.Before;
039import org.junit.Test;
040import org.junit.runner.RunWith;
041import org.kuali.rice.core.api.criteria.EqualPredicate;
042import org.kuali.rice.core.api.criteria.GenericQueryResults;
043import org.kuali.rice.core.api.criteria.GenericQueryResults.Builder;
044import org.kuali.rice.core.api.criteria.QueryByCriteria;
045import org.kuali.rice.krad.data.DataObjectService;
046import org.kuali.rice.krad.data.DataObjectWrapper;
047import org.kuali.rice.krad.data.MaterializeOption;
048import org.kuali.rice.krad.data.metadata.DataObjectMetadata;
049import org.kuali.rice.krad.data.metadata.MetadataChild;
050import org.kuali.rice.krad.data.metadata.impl.DataObjectAttributeRelationshipImpl;
051import org.kuali.rice.krad.data.metadata.impl.DataObjectCollectionImpl;
052import org.kuali.rice.krad.data.metadata.impl.DataObjectRelationshipImpl;
053import org.kuali.rice.krad.data.provider.impl.DataObjectWrapperBase;
054import org.kuali.rice.krad.data.util.ReferenceLinker;
055import org.mockito.Mock;
056import org.mockito.invocation.InvocationOnMock;
057import org.mockito.runners.MockitoJUnitRunner;
058import org.mockito.stubbing.Answer;
059import org.springframework.beans.NullValueInNestedPathException;
060
061@RunWith(MockitoJUnitRunner.class)
062public class DataObjectWrapperBaseTest {
063
064    @Mock private DataObjectService dataObjectService;
065        @Mock
066        private DataObjectMetadata dataObjectMetadata;
067        @Mock
068        private DataObjectMetadata dataObject2Metadata;
069        @Mock
070        private DataObjectMetadata dataObject3Metadata;
071        @Mock
072        private DataObjectMetadata dataObject4Metadata;
073    @Mock private ReferenceLinker referenceLinker;
074
075    private DataObject dataObject;
076    private DataObject2 dataObject2;
077        private DataObject4 dataObject4;
078        private DataObjectWrapperBase<DataObject> wrap;
079
080        @SuppressWarnings({ "unchecked", "rawtypes" })
081        protected void setUpDataObjectMetadataMocks() {
082                when(dataObjectService.supports(DataObject.class)).thenReturn(true);
083                when(dataObjectMetadata.getType()).thenReturn((Class) DataObject.class);
084                when(dataObjectMetadata.getPrimaryKeyAttributeNames()).thenReturn(Collections.singletonList("id"));
085
086                // M:1 relationship, lazy-loaded, not part of parent object tree
087                DataObjectRelationshipImpl dataObject2Relationship = new DataObjectRelationshipImpl();
088                dataObject2Relationship.setName("dataObject2");
089                dataObject2Relationship.setRelatedType(DataObject2.class);
090                dataObject2Relationship.setSavedWithParent(false);
091                dataObject2Relationship.setLoadedAtParentLoadTime(false);
092                dataObject2Relationship.setLoadedDynamicallyUponUse(true);
093                dataObject2Relationship.setAttributeRelationships((List) Collections
094                                .singletonList(new DataObjectAttributeRelationshipImpl("dataObject2sKey", "one")));
095
096                when(dataObjectMetadata.getRelationship("dataObject2")).thenReturn(dataObject2Relationship);
097
098                // M:1 relationship, eager-loaded, not part of parent object tree
099                DataObjectRelationshipImpl eagerDataObject2Relationship = new DataObjectRelationshipImpl();
100                eagerDataObject2Relationship.setName("eagerDataObject2");
101                eagerDataObject2Relationship.setRelatedType(DataObject2.class);
102                eagerDataObject2Relationship.setSavedWithParent(false);
103                eagerDataObject2Relationship.setLoadedAtParentLoadTime(true);
104                eagerDataObject2Relationship.setLoadedDynamicallyUponUse(false);
105                eagerDataObject2Relationship.setAttributeRelationships((List) Collections
106                                .singletonList(new DataObjectAttributeRelationshipImpl("dataObject2sKey", "one")));
107
108                when(dataObjectMetadata.getRelationship("eagerDataObject2")).thenReturn(eagerDataObject2Relationship);
109
110                when(dataObjectMetadata.getRelationships()).thenReturn(
111                                (List) Arrays.asList(dataObject2Relationship, eagerDataObject2Relationship));
112
113                // 1:M relationship, lazy-loaded, saved with parent
114                DataObjectCollectionImpl dataObject3Relationship = new DataObjectCollectionImpl();
115                dataObject3Relationship.setName("dataObject3s");
116                dataObject3Relationship.setRelatedType(DataObject3.class);
117                dataObject3Relationship.setSavedWithParent(true);
118                dataObject3Relationship.setLoadedAtParentLoadTime(false);
119                dataObject3Relationship.setLoadedDynamicallyUponUse(true);
120                dataObject3Relationship.setAttributeRelationships((List) Collections
121                                .singletonList(new DataObjectAttributeRelationshipImpl("id", "parentId")));
122
123                when(dataObjectMetadata.getCollections()).thenReturn((List) Collections.singletonList(dataObject3Relationship));
124                when(dataObjectMetadata.getCollection("dataObject3s")).thenReturn(dataObject3Relationship);
125    }
126    
127        @SuppressWarnings({ "unchecked", "rawtypes" })
128        protected void setUpDataObject2MetadataMocks() {
129                when(dataObjectService.supports(DataObject2.class)).thenReturn(true);
130                when(dataObject2Metadata.getType()).thenReturn((Class) DataObject2.class);
131                when(dataObject2Metadata.getPrimaryKeyAttributeNames()).thenReturn(Collections.singletonList("one"));
132
133                // M:1 relationship, lazy-loaded, not part of parent object tree
134                DataObjectRelationshipImpl dataObject4Relationship = new DataObjectRelationshipImpl();
135                dataObject4Relationship.setName("dataObject4");
136                dataObject4Relationship.setRelatedType(DataObject4.class);
137                dataObject4Relationship.setSavedWithParent(false);
138                dataObject4Relationship.setLoadedAtParentLoadTime(false);
139                dataObject4Relationship.setLoadedDynamicallyUponUse(true);
140                dataObject4Relationship.setAttributeRelationships((List) Collections
141                                .singletonList(new DataObjectAttributeRelationshipImpl("two", "pk")));
142
143                when(dataObject2Metadata.getRelationship("dataObject4")).thenReturn(dataObject4Relationship);
144                when(dataObject2Metadata.getRelationships()).thenReturn((List) Arrays.asList(dataObject4Relationship));
145        }
146
147        @SuppressWarnings({ "unchecked", "rawtypes" })
148        protected void setUpDataObject3MetadataMocks() {
149                when(dataObjectService.supports(DataObject3.class)).thenReturn(true);
150                when(dataObject3Metadata.getType()).thenReturn((Class) DataObject3.class);
151                when(dataObject3Metadata.getPrimaryKeyAttributeNames()).thenReturn(Arrays.asList("parentId", "id"));
152
153                // M:1 relationship, lazy-loaded, not part of parent object tree
154                DataObjectRelationshipImpl dataObject2Relationship = new DataObjectRelationshipImpl();
155                dataObject2Relationship.setName("dataObject2");
156                dataObject2Relationship.setRelatedType(DataObject2.class);
157                dataObject2Relationship.setSavedWithParent(false);
158                dataObject2Relationship.setLoadedAtParentLoadTime(false);
159                dataObject2Relationship.setLoadedDynamicallyUponUse(true);
160                dataObject2Relationship.setAttributeRelationships((List) Collections
161                                .singletonList(new DataObjectAttributeRelationshipImpl("world", "one")));
162
163                when(dataObject3Metadata.getRelationship("dataObject2")).thenReturn(dataObject2Relationship);
164                when(dataObject3Metadata.getRelationships()).thenReturn((List) Arrays.asList(dataObject2Relationship));
165        }
166
167        @SuppressWarnings({ "unchecked", "rawtypes" })
168        protected void setUpDataObject4MetadataMocks() {
169                when(dataObjectService.supports(DataObject4.class)).thenReturn(true);
170                when(dataObject4Metadata.getType()).thenReturn((Class) DataObject4.class);
171                when(dataObject4Metadata.getPrimaryKeyAttributeNames()).thenReturn(Collections.singletonList("pk"));
172        }
173
174    protected void configureMocks() {
175                setUpDataObjectMetadataMocks();
176                setUpDataObject2MetadataMocks();
177                setUpDataObject3MetadataMocks();
178                setUpDataObject4MetadataMocks();
179
180                when(dataObjectService.findMatching(any(Class.class), any(QueryByCriteria.class))).thenAnswer(new Answer() {
181                        @Override
182                        public Object answer(InvocationOnMock invocation) {
183                                Class<?> dataObjectType = (Class<?>) invocation.getArguments()[0];
184                                QueryByCriteria criteria = (QueryByCriteria) invocation.getArguments()[1];
185                                if (DataObject3.class.isAssignableFrom(dataObjectType)) {
186                                        if (criteria.getPredicate() instanceof EqualPredicate
187                                                        && ((EqualPredicate) criteria.getPredicate()).getPropertyPath().equals("parentId")
188                                                        && ((EqualPredicate) criteria.getPredicate()).getValue().getValue().equals("1")) {
189                                                Builder builder = GenericQueryResults.Builder.create();
190                                                builder.setResults(Arrays.asList(
191                                                                new DataObject3("1", "C1", "hello", "world")
192                                                                , new DataObject3("1", "C2", "howdy", "Westeros")));
193                                                builder.setTotalRowCount(2);
194                                                return builder.build();
195                                        }
196                                }
197
198                                // return a completely empty object if unknown
199                                return GenericQueryResults.Builder.create().build();
200                        }
201                });
202
203                when(dataObjectService.find(any(Class.class), any())).thenAnswer(new Answer() {
204                        @Override
205                        public Object answer(InvocationOnMock invocation) {
206                                Class<?> dataObjectType = (Class<?>) invocation.getArguments()[0];
207                                Object primaryKey = invocation.getArguments()[1];
208                                if (DataObject2.class.isAssignableFrom(dataObjectType)) {
209                                        if (primaryKey instanceof String && StringUtils.equals((String) primaryKey, "one")) {
210                                                return dataObject2;
211                                        }
212                                } else if (DataObject4.class.isAssignableFrom(dataObjectType)) {
213                                        if (primaryKey instanceof String && StringUtils.equals((String) primaryKey, "two")) {
214                                                return dataObject4;
215                                        }
216                                }
217
218                                return null;
219                        }
220                });
221
222        // make it so that DataObjectService returns a proper wrap when asked
223                when(dataObjectService.wrap(any())).thenAnswer(new Answer() {
224            @Override
225                        public Object answer(InvocationOnMock invocation) {
226                                Object object = invocation.getArguments()[0];
227                                if (object instanceof DataObject) {
228                                        return new DataObjectWrapperImpl<DataObject>((DataObject) object, dataObjectMetadata,
229                                                        dataObjectService,
230                                                        referenceLinker);
231                                } else if (object instanceof DataObject2) {
232                                        return new DataObjectWrapperImpl<DataObject2>((DataObject2) object, dataObject2Metadata,
233                                                        dataObjectService,
234                                                        referenceLinker);
235                                } else if (object instanceof DataObject3) {
236                                        return new DataObjectWrapperImpl<DataObject3>((DataObject3) object, dataObject3Metadata,
237                                                        dataObjectService, referenceLinker);
238                                } else if (object instanceof DataObject4) {
239                                        return new DataObjectWrapperImpl<DataObject4>((DataObject4) object, dataObject4Metadata,
240                                                        dataObjectService,
241                                                        referenceLinker);
242                                }
243                                return new DataObjectWrapperImpl<Object>(object, null, dataObjectService, referenceLinker);
244            }
245        });
246    }
247    
248    @Before
249    public void setup() throws Exception {
250                dataObject = new DataObject("1", "FieldOne", 2, "one");
251                dataObject2 = new DataObject2("one", "two");
252                dataObject.setDataObject2(dataObject2);
253                dataObject4 = new DataObject4("two", "some other value");
254                wrap = new DataObjectWrapperImpl<DataObject>(dataObject, dataObjectMetadata, dataObjectService,
255                                referenceLinker);
256
257                configureMocks();
258    }
259
260    static final class DataObjectWrapperImpl<T> extends DataObjectWrapperBase<T> {
261        private DataObjectWrapperImpl(T dataObject, DataObjectMetadata metadata, DataObjectService dataObjectService,
262                ReferenceLinker referenceLinker) {
263            super(dataObject, metadata, dataObjectService, referenceLinker);
264        }
265    }
266
267    @Test
268    public void testGetType() {
269        assertEquals(DataObject.class, wrap.getWrappedClass());
270    }
271
272    @Test
273    public void testGetMetadata() {
274                assertEquals(dataObjectMetadata, wrap.getMetadata());
275    }
276
277    @Test
278    public void testGetWrappedInstance() {
279        assertEquals(dataObject, wrap.getWrappedInstance());
280    }
281
282    @Test
283    public void testGetPrimaryKeyValues() {
284        Map<String, Object> primaryKeyValues = wrap.getPrimaryKeyValues();
285        assertEquals(1, primaryKeyValues.size());
286        assertTrue(primaryKeyValues.containsKey("id"));
287        assertEquals("1", primaryKeyValues.get("id"));
288
289                assertEquals("Mismatch on DataObject2's PKs", Collections.singletonMap("one", "one"),
290                                dataObjectService.wrap(dataObject2).getPrimaryKeyValues());
291    }
292
293    @Test
294    public void testEqualsByPrimaryKey() {
295        // first check that it's equal to itself
296        assertTrue(wrap.equalsByPrimaryKey(dataObject));
297
298        // now create one with an equal primary key but different values for non-pk fields, should be euqual
299                assertTrue(wrap.equalsByPrimaryKey(new DataObject("1", "blah", 500, "one")));
300
301        // now create one with a different primary key, should not be equal
302                assertFalse(wrap.equalsByPrimaryKey(new DataObject("2", "FieldOne", 2, "one")));
303
304        // let's do some null checking
305        assertFalse(wrap.equalsByPrimaryKey(null));
306
307        // verify what happens when primary key is null on object being compared
308                assertFalse(wrap.equalsByPrimaryKey(new DataObject(null, null, -1, "one")));
309
310    }
311
312    @Test
313    public void testGetPropertyType_Nested() {
314        assertEquals(DataObject2.class, wrap.getPropertyType("dataObject2"));
315        assertEquals(String.class, wrap.getPropertyType("dataObject2.one"));
316        assertEquals(String.class, wrap.getPropertyType("dataObject2.two"));
317    }
318
319    @Test
320    public void testGetPropertyValueNullSafe() {
321                DataObject dataObject = new DataObject("a", "b", 3, "one");
322                DataObjectWrapper<DataObject> wrap = new DataObjectWrapperImpl<DataObject>(dataObject, dataObjectMetadata,
323                                dataObjectService,
324                referenceLinker);
325        assertNull(wrap.getPropertyValue("dataObject2"));
326
327        //wrap.setPropertyValue("dataObject2.dataObject3", new DataObject3());
328
329        // assert that a NullValueInNestedPathException is thrown
330        try {
331            wrap.getPropertyValue("dataObject2.dataObject3");
332            fail("NullValueInNestedPathException should have been thrown");
333        } catch (NullValueInNestedPathException e) {
334            // this should be thrown!
335        }
336
337        // now do a null-safe check
338        assertNull(wrap.getPropertyValueNullSafe("dataObject2.dataObject3"));
339
340    }
341
342    @Test
343    public void testGetPropertyType_Collection() {
344        // setup stuff
345                DataObject dataObject = new DataObject("a", "b", 3, "one");
346        DataObject3 do3_1 = new DataObject3();
347        do3_1.setHello("hi");
348        do3_1.setWorld("Earth");
349        DataObject3 do3_2 = new DataObject3();
350        do3_2.setHello("howdy");
351        do3_2.setWorld("Westeros");
352        dataObject.getDataObject3s().add(do3_1);
353        dataObject.getDataObject3s().add(do3_2);
354
355        // now check through a collection
356                DataObjectWrapper<DataObject> wrap = new DataObjectWrapperImpl<DataObject>(dataObject, dataObjectMetadata,
357                                dataObjectService,
358                referenceLinker);
359        Class<?> type = wrap.getPropertyType("dataObject3s[0].hello");
360        assertEquals(String.class, type);
361        type = wrap.getPropertyType("dataObject3s[1].world");
362        assertEquals(String.class, type);
363        type = wrap.getPropertyType("dataObject3s[2].world");
364        // should be null because we have nothing at this index
365        assertNull(type);
366    }
367
368        @Test
369        public void testMaterializeOptionMatch_Default() {
370                Collection<MetadataChild> childRelationships = wrap.getChildrenMatchingOptions();
371                assertNotNull("getChildrenMatchingOptions() shoud not return null", childRelationships);
372                assertEquals("getChildrenMatchingOptions() returned wrong number of rows", 1, childRelationships.size());
373                assertEquals("getChildrenMatchingOptions() returned wrong type of relationship",
374                                DataObjectRelationshipImpl.class, childRelationships.iterator().next().getClass());
375                assertEquals("getChildrenMatchingOptions() relationship was for wrong property", "dataObject2",
376                                childRelationships.iterator().next().getName());
377                assertEquals("getChildrenMatchingOptions() relationship was for wrong data type", DataObject2.class,
378                                childRelationships.iterator().next().getRelatedType());
379        }
380
381        @Test
382        public void testMaterializeOptionMatch_WithEager() {
383                Collection<MetadataChild> childRelationships = wrap
384                                .getChildrenMatchingOptions(MaterializeOption.INCLUDE_EAGER_REFS);
385                assertNotNull("getChildrenMatchingOptions() shoud not return null", childRelationships);
386                assertEquals("getChildrenMatchingOptions() returned wrong number of rows", 2, childRelationships.size());
387        }
388
389        @Test
390        public void testMaterializeOptionMatch_CollectionsOnly_NonUpdatable() {
391                Collection<MetadataChild> childRelationships = wrap.getChildrenMatchingOptions(MaterializeOption.COLLECTIONS);
392                assertNotNull("getChildrenMatchingOptions() shoud not return null", childRelationships);
393                assertEquals("getChildrenMatchingOptions() returned wrong number of rows", 0, childRelationships.size());
394        }
395
396        @Test
397        public void testMaterializeOptionMatch_CollectionsOnly_Updatable() {
398                Collection<MetadataChild> childRelationships = wrap.getChildrenMatchingOptions(MaterializeOption.COLLECTIONS,
399                                MaterializeOption.UPDATE_UPDATABLE_REFS);
400                assertNotNull("getChildrenMatchingOptions() shoud not return null", childRelationships);
401                assertEquals("getChildrenMatchingOptions() returned wrong number of rows", 1, childRelationships.size());
402                assertEquals("getChildrenMatchingOptions() returned wrong type of relationship",
403                                DataObjectCollectionImpl.class, childRelationships.iterator().next().getClass());
404                assertEquals("getChildrenMatchingOptions() relationship was for wrong property", "dataObject3s",
405                                childRelationships.iterator().next().getName());
406                assertEquals("getChildrenMatchingOptions() relationship was for wrong data type", DataObject3.class,
407                                childRelationships.iterator().next().getRelatedType());
408        }
409
410        @Test
411        public void testMaterializeOptionMatch_Updatable() {
412                Collection<MetadataChild> childRelationships = wrap
413                                .getChildrenMatchingOptions(MaterializeOption.UPDATE_UPDATABLE_REFS);
414                assertNotNull("getChildrenMatchingOptions() shoud not return null", childRelationships);
415                assertEquals("getChildrenMatchingOptions() returned wrong number of rows", 2, childRelationships.size());
416        }
417
418        @Test
419        public void testMaterialize_Default() {
420                // nulling out reference so it can be reloaded
421                dataObject.setDataObject2(null);
422
423                wrap.materializeReferencedObjects();
424                verify(dataObjectService).supports(DataObject2.class);
425                verify(dataObjectService, never()).supports(DataObject4.class);
426
427                verify(dataObjectService).find(DataObject2.class, "one");
428                assertNotNull("dataObject2 should have been loaded", dataObject.getDataObject2());
429        }
430
431        @Test
432        public void testMaterialize_Recursive() {
433                // nulling out reference so it can be reloaded
434                dataObject.setDataObject2(null);
435
436                wrap.materializeReferencedObjectsToDepth(2);
437                verify(dataObjectService, atLeastOnce()).supports(DataObject2.class);
438                verify(dataObjectService).find(DataObject2.class, "one");
439                verify(dataObjectService, atLeastOnce()).supports(DataObject4.class);
440                verify(dataObjectService).find(DataObject4.class, "two");
441
442                assertNotNull("dataObject2 should have been loaded", dataObject.getDataObject2());
443                assertNotNull("dataObject2.dataObject4 should have been loaded", dataObject.getDataObject2().getDataObject4());
444        }
445
446        @Test
447        public void testMaterialize_Recursive_WithCollections() {
448                // nulling out reference so it can be reloaded
449                dataObject.setDataObject2(null);
450                dataObject.setDataObject3s(null);
451
452                wrap.materializeReferencedObjectsToDepth(2, MaterializeOption.UPDATE_UPDATABLE_REFS);
453                verify(dataObjectService, atLeastOnce()).supports(DataObject2.class);
454                verify(dataObjectService, atLeastOnce()).supports(DataObject3.class);
455                verify(dataObjectService, atLeastOnce()).supports(DataObject4.class);
456
457                verify(dataObjectService).find(DataObject2.class, "one");
458                verify(dataObjectService).find(DataObject4.class, "two");
459
460                assertNotNull("dataObject2 should have been loaded", dataObject.getDataObject2());
461                assertNotNull("dataObject2.dataObject4 should have been loaded", dataObject.getDataObject2().getDataObject4());
462
463                verify(dataObjectService).findMatching(DataObject3.class,
464                                QueryByCriteria.Builder.andAttributes(Collections.singletonMap("parentId", "1")).build());
465
466                assertNotNull("The list of DataObject3 should not have been nulled out", dataObject.getDataObject3s());
467                assertEquals("The list of DataObject3 should have had records", 2, dataObject.getDataObject3s().size());
468
469                // Confirm that it attempted to load each of the child objects of the child records in the collection
470                verify(dataObjectService).find(DataObject2.class, "world");
471                verify(dataObjectService).find(DataObject2.class, "Westeros");
472        }
473
474        @Test
475        public void testMaterialize_InvalidCode_DontNullIt() {
476                assertNotNull("dataObject2 should not be null at start of test", dataObject.getDataObject2());
477
478                // setting the foreign key to an invalid value
479                dataObject.setDataObject2sKey("SOMETHING_INVALID");
480
481                wrap.materializeReferencedObjects();
482                verify(dataObjectService).supports(DataObject2.class);
483                verify(dataObjectService).find(DataObject2.class, "SOMETHING_INVALID");
484                assertNotNull("dataObject2 should not have been nulled out", dataObject.getDataObject2());
485                assertEquals("The object should be the original, with the originals PK", "one", dataObject.getDataObject2()
486                                .getOne());
487        }
488
489        @Test
490        public void testMaterialize_InvalidCode_PleaseNullIt() {
491                assertNotNull("dataObject2 should not be null at start of test", dataObject.getDataObject2());
492
493                // setting the foreign key to an invalid value
494                dataObject.setDataObject2sKey("SOMETHING_INVALID");
495
496                wrap.materializeReferencedObjects(MaterializeOption.NULL_INVALID_REFS);
497                verify(dataObjectService).supports(DataObject2.class);
498                verify(dataObjectService).find(DataObject2.class, "SOMETHING_INVALID");
499                assertNull("dataObject2 should have been nulled out", dataObject.getDataObject2());
500        }
501
502        @Test
503        public void testMaterialize_UpdateUpdatable() {
504                wrap.materializeReferencedObjects(MaterializeOption.UPDATE_UPDATABLE_REFS);
505                verify(dataObjectService).supports(DataObject2.class);
506                verify(dataObjectService).supports(DataObject3.class);
507                verify(dataObjectService).find(DataObject2.class, "one");
508                verify(dataObjectService).findMatching(DataObject3.class,
509                                QueryByCriteria.Builder.andAttributes(Collections.singletonMap("parentId", "1")).build());
510                assertNotNull("The list of DataObject3 should not have been nulled out", dataObject.getDataObject3s());
511        }
512
513    public static final class DataObject {
514
515        private String id;
516        private String fieldOne;
517        private Integer fieldTwo;
518                private String dataObject2sKey;
519        private DataObject2 dataObject2;
520                private DataObject2 eagerDataObject2;
521
522        private List<DataObject3> dataObject3s;
523
524                DataObject(String id, String fieldOne, Integer fieldTwo, String dataObject2sKey) {
525            this.id = id;
526            this.fieldOne = fieldOne;
527            this.fieldTwo = fieldTwo;
528                        this.dataObject2sKey = dataObject2sKey;
529            this.dataObject3s = new ArrayList<DataObject3>();
530        }
531
532        public String getId() {
533            return id;
534        }
535
536        public void setId(String id) {
537            this.id = id;
538        }
539
540        public String getFieldOne() {
541            return fieldOne;
542        }
543
544        public void setFieldOne(String fieldOne) {
545            this.fieldOne = fieldOne;
546        }
547
548        public Integer getFieldTwo() {
549            return fieldTwo;
550        }
551
552        public void setFieldTwo(Integer fieldTwo) {
553            this.fieldTwo = fieldTwo;
554        }
555
556        public DataObject2 getDataObject2() {
557            return dataObject2;
558        }
559
560        public void setDataObject2(DataObject2 dataObject2) {
561            this.dataObject2 = dataObject2;
562        }
563
564        public List<DataObject3> getDataObject3s() {
565            return dataObject3s;
566        }
567
568        public void setDataObject3s(List<DataObject3> dataObject3s) {
569            this.dataObject3s = dataObject3s;
570        }
571
572                public String getDataObject2sKey() {
573                        return dataObject2sKey;
574                }
575
576                public void setDataObject2sKey(String dataObject2sKey) {
577                        this.dataObject2sKey = dataObject2sKey;
578                }
579
580                public DataObject2 getEagerDataObject2() {
581                        return eagerDataObject2;
582                }
583
584                public void setEagerDataObject2(DataObject2 eagerDataObject2) {
585                        this.eagerDataObject2 = eagerDataObject2;
586                }
587    }
588
589    public static final class DataObject2 {
590        private String one;
591                private String two; // FK for data object 4
592                private DataObject4 dataObject4;
593
594        public DataObject2() {}
595
596        public DataObject2(String one, String two) {
597            this.one = one;
598            this.two = two;
599        }
600
601        public String getOne() {
602            return one;
603        }
604
605        public void setOne(String one) {
606            this.one = one;
607        }
608
609        public String getTwo() {
610            return two;
611        }
612
613        public void setTwo(String two) {
614            this.two = two;
615        }
616
617                public DataObject4 getDataObject4() {
618                        return dataObject4;
619        }
620
621                public void setDataObject4(DataObject4 dataObject3) {
622                        this.dataObject4 = dataObject3;
623        }
624    }
625
626        public static final class DataObject4 {
627                private String pk;
628                private String notPk;
629
630                public DataObject4(String pk, String notPk) {
631                        this.pk = pk;
632                        this.notPk = notPk;
633                }
634
635                public String getPk() {
636                        return pk;
637                }
638
639                public void setPk(String pk) {
640                        this.pk = pk;
641                }
642
643                public String getNotPk() {
644                        return notPk;
645                }
646
647                public void setNotPk(String notPk) {
648                        this.notPk = notPk;
649                }
650        }
651
652    public static final class DataObject3 {
653
654                private String parentId;
655                private String id;
656        private String hello;
657                private String world; // FK for data object 2
658                private DataObject2 dataObject2;
659
660                public DataObject3() {
661                }
662
663                public DataObject3(String parentId, String id, String hello, String world) {
664                        super();
665                        this.parentId = parentId;
666                        this.id = id;
667                        this.hello = hello;
668                        this.world = world;
669                }
670
671                public String getHello() {
672            return hello;
673        }
674
675        public void setHello(String hello) {
676            this.hello = hello;
677        }
678
679        public String getWorld() {
680            return world;
681        }
682
683        public void setWorld(String world) {
684            this.world = world;
685        }
686
687                public String getParentId() {
688                        return parentId;
689                }
690
691                public void setParentId(String parentId) {
692                        this.parentId = parentId;
693                }
694
695                public String getId() {
696                        return id;
697                }
698
699                public void setId(String id) {
700                        this.id = id;
701                }
702
703                public DataObject2 getDataObject2() {
704                        return dataObject2;
705                }
706
707                public void setDataObject2(DataObject2 dataObject2) {
708                        this.dataObject2 = dataObject2;
709                }
710
711    }
712
713}