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