1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.data.provider.impl;
17
18 import java.beans.PropertyDescriptor;
19 import java.beans.PropertyEditor;
20 import java.lang.reflect.Field;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 import org.apache.commons.lang.ArrayUtils;
30 import org.apache.commons.lang.StringUtils;
31 import org.kuali.rice.core.api.criteria.QueryByCriteria;
32 import org.kuali.rice.krad.data.CompoundKey;
33 import org.kuali.rice.krad.data.DataObjectService;
34 import org.kuali.rice.krad.data.DataObjectWrapper;
35 import org.kuali.rice.krad.data.MaterializeOption;
36 import org.kuali.rice.krad.data.metadata.DataObjectAttribute;
37 import org.kuali.rice.krad.data.metadata.DataObjectAttributeRelationship;
38 import org.kuali.rice.krad.data.metadata.DataObjectCollection;
39 import org.kuali.rice.krad.data.metadata.DataObjectMetadata;
40 import org.kuali.rice.krad.data.metadata.DataObjectRelationship;
41 import org.kuali.rice.krad.data.metadata.MetadataChild;
42 import org.kuali.rice.krad.data.util.ReferenceLinker;
43 import org.springframework.beans.BeanWrapper;
44 import org.springframework.beans.BeansException;
45 import org.springframework.beans.InvalidPropertyException;
46 import org.springframework.beans.NullValueInNestedPathException;
47 import org.springframework.beans.PropertyAccessorFactory;
48 import org.springframework.beans.PropertyAccessorUtils;
49 import org.springframework.beans.PropertyValue;
50 import org.springframework.beans.PropertyValues;
51 import org.springframework.beans.TypeMismatchException;
52 import org.springframework.core.CollectionFactory;
53 import org.springframework.core.MethodParameter;
54 import org.springframework.core.convert.ConversionService;
55 import org.springframework.core.convert.TypeDescriptor;
56
57 import com.google.common.collect.Sets;
58
59
60
61
62
63
64
65
66 public abstract class DataObjectWrapperBase<T> implements DataObjectWrapper<T> {
67 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DataObjectWrapperBase.class);
68
69 private final T dataObject;
70 private final DataObjectMetadata metadata;
71 private final BeanWrapper wrapper;
72 private final DataObjectService dataObjectService;
73 private final ReferenceLinker referenceLinker;
74
75
76
77
78
79
80
81
82
83 protected DataObjectWrapperBase(T dataObject, DataObjectMetadata metadata, DataObjectService dataObjectService,
84 ReferenceLinker referenceLinker) {
85 this.dataObject = dataObject;
86 this.metadata = metadata;
87 this.dataObjectService = dataObjectService;
88 this.referenceLinker = referenceLinker;
89 this.wrapper = PropertyAccessorFactory.forBeanPropertyAccess(dataObject);
90
91
92
93 }
94
95
96
97
98 @Override
99 public DataObjectMetadata getMetadata() {
100 return metadata;
101 }
102
103
104
105
106
107 @Override
108 public T getWrappedInstance() {
109 return dataObject;
110 }
111
112
113
114
115 @Override
116 public Object getPropertyValueNullSafe(String propertyName) throws BeansException {
117 try {
118 return getPropertyValue(propertyName);
119 } catch (NullValueInNestedPathException e) {
120 return null;
121 }
122 }
123
124
125
126
127 @SuppressWarnings("unchecked")
128 @Override
129 public Class<T> getWrappedClass() {
130 return (Class<T>) wrapper.getWrappedClass();
131 }
132
133
134
135
136 @Override
137 public PropertyDescriptor[] getPropertyDescriptors() {
138 return wrapper.getPropertyDescriptors();
139 }
140
141
142
143
144 @Override
145 public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException {
146 return wrapper.getPropertyDescriptor(propertyName);
147 }
148
149
150
151
152 @Override
153 public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
154 wrapper.setAutoGrowNestedPaths(autoGrowNestedPaths);
155 }
156
157
158
159
160 @Override
161 public boolean isAutoGrowNestedPaths() {
162 return wrapper.isAutoGrowNestedPaths();
163 }
164
165
166
167
168 @Override
169 public void setAutoGrowCollectionLimit(int autoGrowCollectionLimit) {
170 wrapper.setAutoGrowCollectionLimit(autoGrowCollectionLimit);
171 }
172
173
174
175
176 @Override
177 public int getAutoGrowCollectionLimit() {
178 return wrapper.getAutoGrowCollectionLimit();
179 }
180
181
182
183
184 @Override
185 public void setConversionService(ConversionService conversionService) {
186 wrapper.setConversionService(conversionService);
187 }
188
189
190
191
192 @Override
193 public ConversionService getConversionService() {
194 return wrapper.getConversionService();
195 }
196
197
198
199
200 @Override
201 public void setExtractOldValueForEditor(boolean extractOldValueForEditor) {
202 wrapper.setExtractOldValueForEditor(extractOldValueForEditor);
203 }
204
205
206
207
208 @Override
209 public boolean isExtractOldValueForEditor() {
210 return wrapper.isExtractOldValueForEditor();
211 }
212
213
214
215
216 @Override
217 public boolean isReadableProperty(String propertyName) {
218 return wrapper.isReadableProperty(propertyName);
219 }
220
221
222
223
224 @Override
225 public boolean isWritableProperty(String propertyName) {
226 return wrapper.isWritableProperty(propertyName);
227 }
228
229
230
231
232 @Override
233 public Class<?> getPropertyType(String propertyName) throws BeansException {
234 return wrapper.getPropertyType(propertyName);
235 }
236
237
238
239
240 @Override
241 public TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException {
242 return wrapper.getPropertyTypeDescriptor(propertyName);
243 }
244
245
246
247
248 @Override
249 public Object getPropertyValue(String propertyName) throws BeansException {
250 return wrapper.getPropertyValue(propertyName);
251 }
252
253
254
255
256 @Override
257 public void setPropertyValue(String propertyName, Object value) throws BeansException {
258 wrapper.setPropertyValue(propertyName, value);
259 }
260
261
262
263
264 @Override
265 public void setPropertyValue(PropertyValue pv) throws BeansException {
266 wrapper.setPropertyValue(pv);
267 }
268
269
270
271
272 @Override
273 public void setPropertyValues(Map<?, ?> map) throws BeansException {
274 wrapper.setPropertyValues(map);
275 }
276
277
278
279
280 @Override
281 public void setPropertyValues(PropertyValues pvs) throws BeansException {
282 wrapper.setPropertyValues(pvs);
283 }
284
285
286
287
288 @Override
289 public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) throws BeansException {
290 wrapper.setPropertyValues(pvs, ignoreUnknown);
291 }
292
293
294
295
296 @Override
297 public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown,
298 boolean ignoreInvalid) throws BeansException {
299 wrapper.setPropertyValues(pvs, ignoreUnknown, ignoreInvalid);
300 }
301
302
303
304
305 @Override
306 public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
307 wrapper.registerCustomEditor(requiredType, propertyEditor);
308 }
309
310
311
312
313 @Override
314 public void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor) {
315 wrapper.registerCustomEditor(requiredType, propertyPath, propertyEditor);
316 }
317
318
319
320
321 @Override
322 public PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath) {
323 return wrapper.findCustomEditor(requiredType, propertyPath);
324 }
325
326
327
328
329 @Override
330 public <Y> Y convertIfNecessary(Object value, Class<Y> requiredType) throws TypeMismatchException {
331 return wrapper.convertIfNecessary(value, requiredType);
332 }
333
334
335
336
337 @Override
338 public <Y> Y convertIfNecessary(Object value, Class<Y> requiredType,
339 MethodParameter methodParam) throws TypeMismatchException {
340 return wrapper.convertIfNecessary(value, requiredType, methodParam);
341 }
342
343
344
345
346 @Override
347 public <Y> Y convertIfNecessary(Object value, Class<Y> requiredType, Field field) throws TypeMismatchException {
348 return wrapper.convertIfNecessary(value, requiredType, field);
349 }
350
351
352
353
354 @Override
355 public Map<String, Object> getPrimaryKeyValues() {
356 Map<String, Object> primaryKeyValues = new HashMap<String, Object>();
357 if (metadata != null) {
358 List<String> primaryKeyAttributeNames = metadata.getPrimaryKeyAttributeNames();
359 if (primaryKeyAttributeNames != null) {
360 for (String primaryKeyAttributeName : primaryKeyAttributeNames) {
361 primaryKeyValues.put(primaryKeyAttributeName, getPropertyValue(primaryKeyAttributeName));
362 }
363 }
364 } else {
365 LOG.warn("Attempt to retrieve PK fields on object with no metadata: " + dataObject.getClass().getName());
366 }
367 return primaryKeyValues;
368 }
369
370
371
372
373 @Override
374 public Object getPrimaryKeyValue() {
375 if (!areAllPrimaryKeyAttributesPopulated()) {
376 return null;
377 }
378
379 Map<String, Object> primaryKeyValues = getPrimaryKeyValues();
380
381 if (primaryKeyValues.size() == 1) {
382 return primaryKeyValues.values().iterator().next();
383 } else {
384 return new CompoundKey(primaryKeyValues);
385 }
386 }
387
388
389
390
391 @Override
392 public boolean areAllPrimaryKeyAttributesPopulated() {
393 if (metadata != null) {
394 List<String> primaryKeyAttributeNames = metadata.getPrimaryKeyAttributeNames();
395 if (primaryKeyAttributeNames != null) {
396 for (String primaryKeyAttributeName : primaryKeyAttributeNames) {
397 Object propValue = getPropertyValue(primaryKeyAttributeName);
398 if (propValue == null || (propValue instanceof String && StringUtils.isBlank((String) propValue))) {
399 return false;
400 }
401 }
402 }
403 return true;
404 } else {
405 LOG.warn("Attempt to check areAllPrimaryKeyAttributesPopulated on object with no metadata: "
406 + dataObject.getClass().getName());
407 return true;
408 }
409 }
410
411
412
413
414 @Override
415 public boolean areAnyPrimaryKeyAttributesPopulated() {
416 if (metadata != null) {
417 List<String> primaryKeyAttributeNames = metadata.getPrimaryKeyAttributeNames();
418 if (primaryKeyAttributeNames != null) {
419 for (String primaryKeyAttributeName : primaryKeyAttributeNames) {
420 Object propValue = getPropertyValue(primaryKeyAttributeName);
421 if (propValue instanceof String && StringUtils.isNotBlank((String) propValue)) {
422 return true;
423 } else if (propValue != null) {
424 return true;
425 }
426 }
427 }
428 return false;
429 } else {
430 LOG.warn("Attempt to check areAnyPrimaryKeyAttributesPopulated on object with no metadata: "
431 + dataObject.getClass().getName());
432 return true;
433 }
434 }
435
436
437
438
439 @Override
440 public List<String> getUnpopulatedPrimaryKeyAttributeNames() {
441 List<String> emptyKeys = new ArrayList<String>();
442 if (metadata != null) {
443 List<String> primaryKeyAttributeNames = metadata.getPrimaryKeyAttributeNames();
444 if (primaryKeyAttributeNames != null) {
445 for (String primaryKeyAttributeName : primaryKeyAttributeNames) {
446 Object propValue = getPropertyValue(primaryKeyAttributeName);
447 if (propValue == null || (propValue instanceof String && StringUtils.isBlank((String) propValue))) {
448 emptyKeys.add(primaryKeyAttributeName);
449 }
450 }
451 }
452 } else {
453 LOG.warn("Attempt to check getUnpopulatedPrimaryKeyAttributeNames on object with no metadata: "
454 + dataObject.getClass().getName());
455 }
456 return emptyKeys;
457 }
458
459
460
461
462 @Override
463 public boolean equalsByPrimaryKey(T object) {
464 if (object == null) {
465 return false;
466 }
467 DataObjectWrapper<T> wrap = dataObjectService.wrap(object);
468 if (!getWrappedClass().isAssignableFrom(wrap.getWrappedClass())) {
469 throw new IllegalArgumentException("The type of the given data object does not match the type of this " +
470 "data object. Given: " + wrap.getWrappedClass() + ", but expected: " + getWrappedClass());
471 }
472
473 Map<String, Object> localPks = getPrimaryKeyValues();
474 Map<String, Object> givenPks = wrap.getPrimaryKeyValues();
475 for (String localPk : localPks.keySet()) {
476 Object localPkValue = localPks.get(localPk);
477 if (localPkValue == null || !localPkValue.equals(givenPks.get(localPk))) {
478 return false;
479 }
480 }
481 return true;
482 }
483
484
485
486
487 @Override
488 public Object getForeignKeyValue(String relationshipName) {
489 Object foreignKeyAttributeValue = getForeignKeyAttributeValue(relationshipName);
490 if (foreignKeyAttributeValue != null) {
491 return foreignKeyAttributeValue;
492 }
493
494
495 Object relationshipObject = getPropertyValue(relationshipName);
496 if (relationshipObject == null) {
497 return null;
498 }
499 return dataObjectService.wrap(relationshipObject).getPrimaryKeyValue();
500 }
501
502
503
504
505 @Override
506 public Object getForeignKeyAttributeValue(String relationshipName) {
507 Map<String, Object> attributeMap = getForeignKeyAttributeMap(relationshipName);
508 if (attributeMap == null) {
509 return null;
510 }
511 return asSingleKey(attributeMap);
512 }
513
514
515
516
517
518
519
520 public Map<String, Object> getForeignKeyAttributeMap(String relationshipName) {
521 MetadataChild relationship = findAndValidateRelationship(relationshipName);
522 List<DataObjectAttributeRelationship> attributeRelationships = relationship.getAttributeRelationships();
523
524 if (!attributeRelationships.isEmpty()) {
525 Map<String, Object> attributeMap = new LinkedHashMap<String, Object>();
526
527 for (DataObjectAttributeRelationship attributeRelationship : attributeRelationships) {
528
529 String parentAttributeName = attributeRelationship.getParentAttributeName();
530 Object parentAttributeValue = null;
531
532 try {
533 parentAttributeValue = getPropertyValue(parentAttributeName);
534 } catch (BeansException be) {
535
536
537 }
538
539
540 if (parentAttributeValue == null) {
541 return null;
542 }
543
544
545 String childAttributeName = attributeRelationship.getChildAttributeName();
546 if (childAttributeName != null) {
547 attributeMap.put(childAttributeName, parentAttributeValue);
548 }
549 }
550
551 return attributeMap;
552 }
553
554 return null;
555 }
556
557
558
559
560
561
562
563
564 private Object asSingleKey(Map<String, Object> keyValues) {
565 if (keyValues.size() == 1) {
566 return keyValues.values().iterator().next();
567 }
568
569 return new CompoundKey(keyValues);
570 }
571
572
573
574
575 @Override
576 public Class<?> getPropertyTypeNullSafe(Class<?> objectType, String propertyName) {
577 DataObjectMetadata objectMetadata = dataObjectService.getMetadataRepository().getMetadata(objectType);
578 return getPropertyTypeChild(objectMetadata,propertyName);
579 }
580
581
582
583
584
585
586
587
588 private Class<?> getPropertyTypeChild(DataObjectMetadata objectMetadata, String propertyName){
589 if(PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName)){
590 String attributePrefix = StringUtils.substringBefore(propertyName,".");
591 String attributeName = StringUtils.substringAfter(propertyName,".");
592
593 if(StringUtils.isNotBlank(attributePrefix) && StringUtils.isNotBlank(attributeName) &&
594 objectMetadata!= null){
595 Class<?> propertyType = traverseRelationship(objectMetadata,attributePrefix,attributeName);
596 if(propertyType != null){
597 return propertyType;
598 }
599 }
600 }
601 return getPropertyType(propertyName);
602 }
603
604
605
606
607
608
609
610
611
612 private Class<?> traverseRelationship(DataObjectMetadata objectMetadata,String attributePrefix,
613 String attributeName){
614 DataObjectRelationship rd = objectMetadata.getRelationship(attributePrefix);
615 if(rd != null){
616 DataObjectMetadata relatedObjectMetadata =
617 dataObjectService.getMetadataRepository().getMetadata(rd.getRelatedType());
618 if(relatedObjectMetadata != null){
619 if(PropertyAccessorUtils.isNestedOrIndexedProperty(attributeName)){
620 return getPropertyTypeChild(relatedObjectMetadata,attributeName);
621 } else{
622 if(relatedObjectMetadata.getAttribute(attributeName) == null &&
623 relatedObjectMetadata.getRelationship(attributeName)!=null){
624 DataObjectRelationship relationship = relatedObjectMetadata.getRelationship(attributeName);
625 return relationship.getRelatedType();
626 }
627 return relatedObjectMetadata.getAttribute(attributeName).getDataType().getType();
628 }
629 }
630 }
631 return null;
632 }
633
634
635
636
637 @Override
638 public void linkChanges(Set<String> changedPropertyPaths) {
639 referenceLinker.linkChanges(getWrappedInstance(), changedPropertyPaths);
640 }
641
642
643
644
645 @Override
646 public void linkForeignKeys(boolean onlyLinkReadOnly) {
647 linkForeignKeysInternalWrapped(this, onlyLinkReadOnly, Sets.newHashSet());
648 }
649
650
651
652
653
654
655
656
657 protected void linkForeignKeysInternal(Object object, boolean onlyLinkReadOnly, Set<Object> linked) {
658 if (object == null || linked.contains(object) || !dataObjectService.supports(object.getClass())) {
659 return;
660 }
661 linked.add(object);
662 DataObjectWrapper<?> wrapped = dataObjectService.wrap(object);
663 linkForeignKeysInternalWrapped(wrapped, onlyLinkReadOnly, linked);
664 }
665
666
667
668
669
670
671
672
673 protected void linkForeignKeysInternalWrapped(DataObjectWrapper<?> wrapped, boolean onlyLinkReadOnly, Set<Object> linked) {
674 List<DataObjectRelationship> relationships = wrapped.getMetadata().getRelationships();
675 for (DataObjectRelationship relationship : relationships) {
676 String relationshipName = relationship.getName();
677 Object relationshipValue = wrapped.getPropertyValue(relationshipName);
678
679
680 if (relationship.isSavedWithParent()) {
681
682 linkForeignKeysInternal(relationshipValue, onlyLinkReadOnly, linked);
683 }
684
685
686 linkForeignKeysInternal(wrapped, relationship, relationshipValue, onlyLinkReadOnly);
687 }
688 List<DataObjectCollection> collections = wrapped.getMetadata().getCollections();
689 for (DataObjectCollection collection : collections) {
690 String relationshipName = collection.getName();
691
692
693 if (collection.isSavedWithParent()) {
694 Collection<?> collectionValue = (Collection<?>)wrapped.getPropertyValue(relationshipName);
695 if (collectionValue != null) {
696 for (Object object : collectionValue) {
697 linkForeignKeysInternal(object, onlyLinkReadOnly, linked);
698 }
699 }
700 }
701 }
702
703 }
704
705
706
707
708 @Override
709 public void fetchRelationship(String relationshipName) {
710 fetchRelationship(relationshipName, true, true);
711 }
712
713
714
715
716 @Override
717 public void fetchRelationship(String relationshipName, boolean useForeignKeyAttribute, boolean nullifyDanglingRelationship) {
718 fetchRelationship(findAndValidateRelationship(relationshipName), useForeignKeyAttribute,
719 nullifyDanglingRelationship);
720 }
721
722
723
724
725
726
727
728 protected void fetchRelationship(MetadataChild relationship, boolean useForeignKeyAttribute, boolean nullifyDanglingRelationship) {
729 Class<?> relatedType = relationship.getRelatedType();
730 if (!dataObjectService.supports(relatedType)) {
731 LOG.warn("Encountered a related type that is not supported by DataObjectService, fetch "
732 + "relationship will do nothing: " + relatedType);
733 return;
734 }
735
736 if (useForeignKeyAttribute) {
737 fetchRelationshipUsingAttributes(relationship, nullifyDanglingRelationship);
738 } else {
739 fetchRelationshipUsingIdentity(relationship, nullifyDanglingRelationship);
740 }
741 }
742
743
744
745
746
747
748
749 protected void fetchRelationshipUsingAttributes(MetadataChild relationship, boolean nullifyDanglingRelationship) {
750 Class<?> relatedType = relationship.getRelatedType();
751 if (relationship.getAttributeRelationships().isEmpty()) {
752 LOG.warn("Attempted to fetch a relationship using a foreign key attribute "
753 + "when one does not exist: "
754 + relationship.getName());
755 } else {
756 Object fetchedValue = null;
757 if (relationship instanceof DataObjectRelationship) {
758 Object foreignKey = getForeignKeyAttributeValue(relationship.getName());
759 if (foreignKey != null) {
760 fetchedValue = dataObjectService.find(relatedType, foreignKey);
761 }
762 } else if (relationship instanceof DataObjectCollection) {
763 Map<String, Object> foreignKeyAttributeMap = getForeignKeyAttributeMap(relationship.getName());
764 fetchedValue = dataObjectService.findMatching(relatedType,
765 QueryByCriteria.Builder.andAttributes(foreignKeyAttributeMap).build()).getResults();
766 }
767 if (fetchedValue != null || nullifyDanglingRelationship) {
768 setPropertyValue(relationship.getName(), fetchedValue);
769 }
770 }
771 }
772
773
774
775
776
777
778
779 protected void fetchRelationshipUsingIdentity(MetadataChild relationship, boolean nullifyDanglingRelationship) {
780 Object propertyValue = getPropertyValue(relationship.getName());
781 if (propertyValue != null) {
782 if (!dataObjectService.supports(propertyValue.getClass())) {
783 throw new IllegalArgumentException("Attempting to fetch an invalid relationship, must be a"
784 + "DataObjectRelationship when fetching without a foreign key");
785 }
786 DataObjectWrapper<?> wrappedRelationship = dataObjectService.wrap(propertyValue);
787 Map<String, Object> primaryKeyValues = wrappedRelationship.getPrimaryKeyValues();
788 Object newPropertyValue = dataObjectService.find(wrappedRelationship.getWrappedClass(),
789 new CompoundKey(primaryKeyValues));
790 if (newPropertyValue != null || nullifyDanglingRelationship) {
791 propertyValue = newPropertyValue;
792 setPropertyValue(relationship.getName(), propertyValue);
793 }
794 }
795
796
797
798 linkForeignKeysInternal(this, relationship, propertyValue, false);
799 populateInverseRelationship(relationship, propertyValue);
800 }
801
802
803
804
805 @Override
806 public void linkForeignKeys(String relationshipName, boolean onlyLinkReadOnly) {
807 MetadataChild relationship = findAndValidateRelationship(relationshipName);
808 Object propertyValue = getPropertyValue(relationshipName);
809 linkForeignKeysInternal(this, relationship, propertyValue, onlyLinkReadOnly);
810 }
811
812
813
814
815 @Override
816 public void materializeReferencedObjects(MaterializeOption... options) {
817 materializeReferencedObjectsToDepth(1, options);
818 }
819
820
821
822
823 @Override
824 public void materializeReferencedObjectsToDepth(int maxDepth, MaterializeOption... options) {
825 boolean setInvalidRefsToNull = ArrayUtils.contains(options, MaterializeOption.NULL_INVALID_REFS);
826 Collection<MetadataChild> matchingChildRelationships = getChildrenMatchingOptions(options);
827
828 for (MetadataChild child : matchingChildRelationships) {
829 fetchRelationship(child, true, setInvalidRefsToNull);
830
831 if (maxDepth > 1) {
832 Object childValue = getPropertyValue(child.getName());
833 if (childValue != null) {
834 if (!(childValue instanceof Collection)) {
835 DataObjectWrapper<Object> childWrapper = dataObjectService.wrap(childValue);
836
837 if (childWrapper.getMetadata() != null) {
838 childWrapper.materializeReferencedObjectsToDepth(maxDepth - 1, options);
839 }
840 } else {
841
842 for (Object collectionElement : (Collection<?>) childValue) {
843 DataObjectWrapper<Object> childWrapper = dataObjectService.wrap(collectionElement);
844
845 if (childWrapper.getMetadata() != null) {
846 childWrapper.materializeReferencedObjectsToDepth(maxDepth - 1, options);
847 }
848 }
849 }
850 }
851 }
852 }
853 }
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870 public Collection<MetadataChild> getChildrenMatchingOptions(MaterializeOption... options) {
871 Collection<MetadataChild> matchingChildren = new ArrayList<>();
872 if (metadata == null) {
873 return matchingChildren;
874 }
875 boolean materializeUpdatable = ArrayUtils.contains(options, MaterializeOption.UPDATE_UPDATABLE_REFS);
876 boolean rematerializeEagerRefs = ArrayUtils.contains(options, MaterializeOption.INCLUDE_EAGER_REFS);
877
878 boolean includeRelationships = ArrayUtils.contains(options, MaterializeOption.REFERENCES)
879 || !ArrayUtils.contains(options, MaterializeOption.COLLECTIONS);
880 boolean includeCollections = ArrayUtils.contains(options, MaterializeOption.COLLECTIONS)
881 || !ArrayUtils.contains(options, MaterializeOption.REFERENCES);
882
883 if (includeRelationships) {
884 for (DataObjectRelationship rel : metadata.getRelationships()) {
885
886
887
888 if (rel.isSavedWithParent() && !materializeUpdatable) {
889 continue;
890 }
891
892
893 if (rel.isLoadedAtParentLoadTime() && !rematerializeEagerRefs) {
894 continue;
895 }
896
897 matchingChildren.add(rel);
898 }
899 }
900
901 if (includeCollections) {
902 for (DataObjectCollection rel : metadata.getCollections()) {
903
904
905
906 if (rel.isSavedWithParent() && !materializeUpdatable) {
907 continue;
908 }
909
910
911 if (rel.isLoadedAtParentLoadTime() && !rematerializeEagerRefs) {
912 continue;
913 }
914
915 matchingChildren.add(rel);
916 }
917 }
918
919 return matchingChildren;
920 }
921
922
923
924
925
926
927
928
929
930
931
932
933
934 protected void linkForeignKeysInternal(DataObjectWrapper<?> wrapped, MetadataChild relationship,
935 Object relationshipValue, boolean onlyLinkReadOnly) {
936 if (!relationship.getAttributeRelationships().isEmpty()) {
937
938 DataObjectWrapper<?> wrappedRelationship = null;
939 if (relationshipValue != null) {
940 wrappedRelationship = dataObjectService.wrap(relationshipValue);
941 }
942 for (DataObjectAttributeRelationship attributeRelationship : relationship.getAttributeRelationships()) {
943 String parentAttributeName = attributeRelationship.getParentAttributeName();
944
945
946 Object childAttributeValue = null;
947 if (wrappedRelationship != null) {
948 childAttributeValue =
949 wrappedRelationship.getPropertyValue(attributeRelationship.getChildAttributeName());
950 }
951 if (onlyLinkReadOnly) {
952 DataObjectAttribute attribute = wrapped.getMetadata().getAttribute(parentAttributeName);
953 if (attribute.isReadOnly()) {
954 wrapped.setPropertyValue(parentAttributeName, childAttributeValue);
955 }
956 } else {
957 wrapped.setPropertyValue(parentAttributeName, childAttributeValue);
958 }
959 }
960 }
961 }
962
963
964
965
966
967
968
969 protected void populateInverseRelationship(MetadataChild relationship, Object propertyValue) {
970 if (propertyValue != null) {
971 MetadataChild inverseRelationship = relationship.getInverseRelationship();
972 if (inverseRelationship != null) {
973 DataObjectWrapper<?> wrappedRelationship = dataObjectService.wrap(propertyValue);
974 if (inverseRelationship instanceof DataObjectCollection) {
975 DataObjectCollection collectionRelationship = (DataObjectCollection)inverseRelationship;
976 String colRelName = inverseRelationship.getName();
977 Collection<Object> collection =
978 (Collection<Object>)wrappedRelationship.getPropertyValue(colRelName);
979 if (collection == null) {
980
981 collection =
982 CollectionFactory.createCollection(wrappedRelationship.getPropertyType(colRelName), 1);
983 wrappedRelationship.setPropertyValue(colRelName, collection);
984 }
985 collection.add(getWrappedInstance());
986 }
987 }
988 }
989 }
990
991
992
993
994
995
996
997 private MetadataChild findAndValidateRelationship(String relationshipName) {
998 if (StringUtils.isBlank(relationshipName)) {
999 throw new IllegalArgumentException("The relationshipName must not be null or blank");
1000 }
1001
1002 MetadataChild relationship = getMetadata().getRelationship(relationshipName);
1003 if (relationship == null) {
1004 relationship = getMetadata().getCollection(relationshipName);
1005 if (relationship == null) {
1006 throw new IllegalArgumentException("Failed to locate a valid relationship from " + getWrappedClass()
1007 + " with the given relationship name '" + relationshipName + "'");
1008 }
1009 }
1010 return relationship;
1011 }
1012
1013 }