1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.datadictionary;
17
18 import java.io.Serializable;
19 import java.util.ArrayList;
20 import java.util.HashSet;
21 import java.util.LinkedHashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.kuali.rice.krad.data.KradDataServiceLocator;
28 import org.kuali.rice.krad.data.metadata.DataObjectAttribute;
29 import org.kuali.rice.krad.data.metadata.DataObjectAttributeRelationship;
30 import org.kuali.rice.krad.data.metadata.DataObjectCollection;
31 import org.kuali.rice.krad.data.metadata.DataObjectMetadata;
32 import org.kuali.rice.krad.data.metadata.DataObjectRelationship;
33 import org.kuali.rice.krad.data.provider.MetadataProvider;
34 import org.kuali.rice.krad.data.provider.annotation.UifDisplayHint;
35 import org.kuali.rice.krad.data.provider.annotation.UifDisplayHintType;
36 import org.kuali.rice.krad.datadictionary.exception.DuplicateEntryException;
37 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
38 import org.kuali.rice.krad.datadictionary.state.StateMapping;
39 import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
40 import org.kuali.rice.krad.exception.ValidationException;
41 import org.springframework.beans.BeanUtils;
42
43
44
45
46
47
48 abstract public class DataDictionaryEntryBase extends DictionaryBeanBase implements DataDictionaryEntry, Serializable {
49 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DataDictionaryEntryBase.class);
50 private static final long serialVersionUID = 5133059101016080533L;
51
52 protected DataObjectMetadata dataObjectMetadata;
53
54 protected List<AttributeDefinition> attributes;
55 protected List<ComplexAttributeDefinition> complexAttributes;
56 protected List<CollectionDefinition> collections;
57 protected List<RelationshipDefinition> relationships;
58 protected Map<String, AttributeDefinition> attributeMap;
59 protected Map<String, ComplexAttributeDefinition> complexAttributeMap;
60 protected Map<String, CollectionDefinition> collectionMap;
61
62 protected Map<String, RelationshipDefinition> relationshipMap;
63
64 protected StateMapping stateMapping;
65
66 public DataDictionaryEntryBase() {
67 this.attributes = new ArrayList<AttributeDefinition>();
68 this.complexAttributes = new ArrayList<ComplexAttributeDefinition>();
69 this.collections = new ArrayList<CollectionDefinition>();
70 this.relationships = new ArrayList<RelationshipDefinition>();
71 this.attributeMap = new LinkedHashMap<String, AttributeDefinition>();
72 this.complexAttributeMap = new LinkedHashMap<String, ComplexAttributeDefinition>();
73 this.collectionMap = new LinkedHashMap<String, CollectionDefinition>();
74 this.relationshipMap = new LinkedHashMap<String, RelationshipDefinition>();
75 }
76
77
78 public abstract Class<?> getEntryClass();
79
80
81
82
83
84 @Override
85 public AttributeDefinition getAttributeDefinition(String attributeName) {
86 if (StringUtils.isBlank(attributeName)) {
87 throw new IllegalArgumentException("invalid (blank) attributeName");
88 }
89 return attributeMap.get(attributeName);
90 }
91
92
93
94
95
96 @BeanTagAttribute(name = "attributes", type = BeanTagAttribute.AttributeType.LISTBEAN)
97 public List<AttributeDefinition> getAttributes() {
98 return this.attributes;
99 }
100
101
102
103
104 public List<ComplexAttributeDefinition> getComplexAttributes() {
105 return this.complexAttributes;
106 }
107
108
109
110
111 public void setComplexAttributes(List<ComplexAttributeDefinition> complexAttributes) {
112 complexAttributeMap.clear();
113 for (ComplexAttributeDefinition complexAttribute : complexAttributes) {
114 if (complexAttribute == null) {
115 throw new DataDictionaryException("invalid (null) complexAttributeDefinition on " + this);
116 }
117 String complexAttributeName = complexAttribute.getName();
118 if (StringUtils.isBlank(complexAttributeName)) {
119 throw new DataDictionaryException("invalid (blank) complexAttributeName on " + this);
120 }
121
122 if (complexAttributeMap.containsKey(complexAttribute)) {
123 throw new DuplicateEntryException("complex attribute '"
124 + complexAttribute
125 + "' already defined as an complex attribute for class '"
126 + getEntryClass().getName()
127 + "'");
128 } else if (collectionMap.containsKey(complexAttributeName)) {
129 throw new DuplicateEntryException("complex attribute '"
130 + complexAttributeName
131 + "' already defined as a Collection for class '"
132 + getEntryClass().getName()
133 + "'");
134 } else if (attributeMap.containsKey(complexAttributeName)) {
135 throw new DuplicateEntryException("complex attribute '"
136 + complexAttributeName
137 + "' already defined as an Attribute for class '"
138 + getEntryClass().getName()
139 + "'");
140 }
141
142 complexAttributeMap.put(complexAttributeName, complexAttribute);
143
144 }
145
146 this.complexAttributes = complexAttributes;
147 }
148
149
150
151
152
153 public CollectionDefinition getCollectionDefinition(String collectionName) {
154 if (StringUtils.isBlank(collectionName)) {
155 throw new IllegalArgumentException("invalid (blank) collectionName");
156 }
157 return collectionMap.get(collectionName);
158 }
159
160
161
162
163
164 @BeanTagAttribute(name = "collections", type = BeanTagAttribute.AttributeType.LISTBEAN)
165 public List<CollectionDefinition> getCollections() {
166 return this.collections;
167 }
168
169
170
171
172
173 public RelationshipDefinition getRelationshipDefinition(String relationshipName) {
174 if (StringUtils.isBlank(relationshipName)) {
175 throw new IllegalArgumentException("invalid (blank) relationshipName");
176 }
177 return getRelationshipMap().get(relationshipName);
178 }
179
180
181
182
183
184 @Override
185 @BeanTagAttribute(name = "relationships", type = BeanTagAttribute.AttributeType.LISTBEAN)
186 public List<RelationshipDefinition> getRelationships() {
187 return this.relationships;
188 }
189
190
191
192
193 @Override
194 public void completeValidation() {
195 completeValidation( new ValidationTrace() );
196 }
197
198 protected void embedMetadata() {
199
200
201 MetadataProvider metadataProvider = KradDataServiceLocator.getProviderRegistry().getMetadataProvider(getEntryClass());
202 if ( metadataProvider != null ) {
203 dataObjectMetadata = metadataProvider.getMetadataForType(getEntryClass());
204 if ( dataObjectMetadata == null ) {
205 LOG.warn( "No metadata defined for " + getEntryClass() + " even though provider returned." );
206 } else {
207
208
209
210
211 injectMetadataIntoAttributes(dataObjectMetadata);
212 injectMetadataIntoCollections(dataObjectMetadata);
213 injectMetadataIntoRelationships(dataObjectMetadata);
214 }
215 } else {
216 LOG.info( "No metadata provider exists which handles: " + getEntryClass());
217 }
218 }
219
220
221
222
223
224
225
226
227 protected void injectMetadataIntoRelationships(DataObjectMetadata dataObjectMetadata) {
228 List<RelationshipDefinition> relationships = getRelationships();
229 boolean relationshipsChanged = false;
230 if ( relationships == null ) {
231 relationships = new ArrayList<RelationshipDefinition>();
232 }
233 for ( DataObjectRelationship rel : dataObjectMetadata.getRelationships() ) {
234 if ( rel.getAttributeRelationships().isEmpty() ) {
235
236 continue;
237 }
238 if ( StringUtils.isNotBlank(rel.getName()) ) {
239 RelationshipDefinition relationshipDefinition = getRelationshipDefinition(rel.getName());
240
241
242 if ( relationshipDefinition == null ){
243 relationshipDefinition = new RelationshipDefinition();
244 relationshipDefinition.setObjectAttributeName(rel.getName());
245 relationshipDefinition.setSourceClass(getEntryClass());
246 relationshipDefinition.setTargetClass(rel.getRelatedType());
247 for ( DataObjectAttributeRelationship attrRel : rel.getAttributeRelationships() ) {
248 PrimitiveAttributeDefinition attrDef = new PrimitiveAttributeDefinition();
249 attrDef.setSourceName(attrRel.getParentAttributeName());
250 attrDef.setTargetName(attrRel.getChildAttributeName());
251 relationshipDefinition.getPrimitiveAttributes().add(attrDef);
252 }
253 relationshipDefinition.setGeneratedFromMetadata(true);
254 relationshipDefinition.setEmbeddedDataObjectMetadata(true);
255 relationships.add(relationshipDefinition);
256 relationshipsChanged = true;
257 }
258 } else {
259 LOG.warn( "Relationship in metadata model contained blank name attribute: " + rel );
260 }
261 }
262 }
263
264 protected void injectMetadataIntoCollections(DataObjectMetadata dataObjectMetadata) {
265 List<CollectionDefinition> collections = getCollections();
266 boolean collectionsChanged = false;
267 if ( collections == null ) {
268 collections = new ArrayList<CollectionDefinition>();
269 }
270 for ( DataObjectCollection coll : dataObjectMetadata.getCollections() ) {
271 if ( StringUtils.isNotBlank(coll.getName()) ) {
272
273
274 if ( getAttributeDefinition(coll.getName()) != null ) {
275 continue;
276 }
277 CollectionDefinition collectionDefinition = getCollectionDefinition(coll.getName());
278
279 if ( collectionDefinition == null ) {
280 collectionDefinition = new CollectionDefinition();
281 collectionDefinition.setName(coll.getName());
282 collectionDefinition.setDataObjectClass(coll.getRelatedType().getName());
283 collectionDefinition.setGeneratedFromMetadata(true);
284 collections.add(collectionDefinition);
285
286 collectionsChanged = true;
287 }
288 collectionDefinition.setDataObjectCollection(coll);
289 collectionDefinition.setEmbeddedDataObjectMetadata(true);
290 } else {
291 LOG.warn( "Relationship in metadata model contained blank name attribute: " + coll );
292 }
293 }
294
295
296 if ( collectionsChanged ) {
297 setCollections(collections);
298 }
299 }
300
301 protected static final Set<String> EXCLUDED_PROPERTY_NAMES = new HashSet<String>();
302 static {
303 EXCLUDED_PROPERTY_NAMES.add("objectId");
304 EXCLUDED_PROPERTY_NAMES.add("versionNumber");
305 }
306
307 protected void injectMetadataIntoAttributes( DataObjectMetadata dataObjectMetadata ) {
308 List<AttributeDefinition> originalDataObjectEntryAttributes = getAttributes();
309
310
311 if ( originalDataObjectEntryAttributes == null ) {
312 originalDataObjectEntryAttributes = new ArrayList<AttributeDefinition>();
313 }
314
315 List<AttributeDefinition> dataObjectEntryAttributes = new ArrayList<AttributeDefinition>();
316
317
318
319 for ( DataObjectAttribute attr : dataObjectMetadata.getAttributes() ) {
320 if ( StringUtils.isBlank(attr.getName())) {
321 LOG.warn( "Attribute in metadata model contained blank name attribute: " + attr );
322 continue;
323 }
324
325 if ( EXCLUDED_PROPERTY_NAMES.contains( attr.getName() ) ) {
326 continue;
327 }
328
329 if ( hasExcludedHint(attr) ) {
330 continue;
331 }
332
333 AttributeDefinition attributeDefinition = getAttributeDefinition(attr.getName());
334 originalDataObjectEntryAttributes.remove(attributeDefinition);
335
336 if ( attributeDefinition == null ) {
337 attributeDefinition = new AttributeDefinition();
338 attributeDefinition.setName(attr.getName());
339 attributeDefinition.setGeneratedFromMetadata(true);
340 }
341
342 attributeDefinition.setDataObjectAttribute(attr);
343 attributeDefinition.setEmbeddedDataObjectMetadata(true);
344 dataObjectEntryAttributes.add(attributeDefinition);
345 }
346
347 dataObjectEntryAttributes.addAll(originalDataObjectEntryAttributes);
348
349
350 setAttributes(dataObjectEntryAttributes);
351 }
352
353
354
355
356
357 protected boolean hasExcludedHint( DataObjectAttribute attr ) {
358 if ( attr.getDisplayHints() != null ) {
359 for ( UifDisplayHint hint : attr.getDisplayHints() ) {
360 if ( hint.value().equals(UifDisplayHintType.EXCLUDE) ) {
361 return true;
362 }
363 }
364 }
365 return false;
366 }
367
368 @Override
369 public void dataDictionaryPostProcessing() {
370 super.dataDictionaryPostProcessing();
371 embedMetadata();
372 if (relationships != null) {
373 relationshipMap.clear();
374
375 for (RelationshipDefinition relationship : relationships) {
376 if (relationship == null) {
377 LOG.warn("Skipping invalid (null) relationshipDefinition on " + this);
378 continue;
379 }
380
381 String relationshipName = relationship.getObjectAttributeName();
382 if (StringUtils.isBlank(relationshipName)) {
383 LOG.warn("Skipping invalid relationshipDefinition with blank relationshipName on " + this);
384 continue;
385 }
386
387 relationship.setSourceClass(getEntryClass());
388 relationshipMap.put(relationshipName, relationship);
389 }
390 }
391
392
393 if (complexAttributes != null) {
394 for (ComplexAttributeDefinition complexAttribute : complexAttributes) {
395 if ( complexAttribute != null ) {
396 addNestedAttributes(complexAttribute, complexAttribute.getName());
397 }
398 }
399 }
400
401 for (AttributeDefinition definition : getAttributes()) {
402 definition.dataDictionaryPostProcessing();
403 }
404
405 for (CollectionDefinition definition : getCollections()) {
406 definition.dataDictionaryPostProcessing();
407 }
408
409 for (RelationshipDefinition definition : getRelationships()) {
410 definition.dataDictionaryPostProcessing();
411 }
412 }
413
414
415
416
417
418
419
420 @Override
421 public void completeValidation(ValidationTrace tracer) {
422 if ( getEntryClass() != null ) {
423 if ( LOG.isDebugEnabled() ) {
424 LOG.debug( "Processing Validation for " + this.getClass().getSimpleName() + " for class: " + getEntryClass().getName() );
425 }
426 tracer.addBean(this.getClass().getSimpleName(), getEntryClass().getSimpleName());
427 for (AttributeDefinition definition : getAttributes()) {
428 definition.completeValidation(getEntryClass(), null, tracer.getCopy());
429 }
430 for (CollectionDefinition definition : getCollections()) {
431 definition.completeValidation(getEntryClass(), null, tracer.getCopy());
432 }
433 for (RelationshipDefinition definition : getRelationships()) {
434 definition.completeValidation(getEntryClass(), null, tracer.getCopy());
435 }
436 } else {
437 tracer.addBean(this.getClass().getSimpleName(), toString() );
438 tracer.createError("DataObjectClass is not set, remaining validations aborted", null );
439 }
440 }
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490 public void setAttributes(List<AttributeDefinition> attributes) {
491 attributeMap.clear();
492 for (AttributeDefinition attribute : attributes) {
493 if (attribute == null) {
494 throw new IllegalArgumentException("invalid (null) attributeDefinition");
495 }
496 String attributeName = attribute.getName();
497 if (StringUtils.isBlank(attributeName)) {
498 throw new ValidationException("invalid (blank) attributeName");
499 }
500
501 if (attributeMap.containsKey(attributeName)) {
502 throw new DuplicateEntryException("attribute '"
503 + attributeName
504 + "' already defined as an Attribute for class '"
505 + getEntryClass().getName()
506 + "'");
507 } else if (collectionMap.containsKey(attributeName)) {
508 throw new DuplicateEntryException("attribute '"
509 + attributeName
510 + "' already defined as a Collection for class '"
511 + getEntryClass().getName()
512 + "'");
513 } else if (complexAttributeMap.containsKey(attributeName)) {
514 throw new DuplicateEntryException("attribute '"
515 + attributeName
516 + "' already defined as an Complex Attribute for class '"
517 + getEntryClass().getName()
518 + "'");
519 }
520 attributeMap.put(attributeName, attribute);
521 }
522 this.attributes = attributes;
523 }
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553 public void setCollections(List<CollectionDefinition> collections) {
554 collectionMap.clear();
555 for (CollectionDefinition collection : collections) {
556 if (collection == null) {
557 throw new IllegalArgumentException("invalid (null) collectionDefinition");
558 }
559 String collectionName = collection.getName();
560 if (StringUtils.isBlank(collectionName)) {
561 throw new ValidationException("invalid (blank) collectionName");
562 }
563
564 if (collectionMap.containsKey(collectionName)) {
565 throw new DuplicateEntryException("collection '"
566 + collectionName
567 + "' already defined for class '"
568 + getEntryClass().getName()
569 + "'");
570 } else if (attributeMap.containsKey(collectionName)) {
571 throw new DuplicateEntryException("collection '"
572 + collectionName
573 + "' already defined as an Attribute for class '"
574 + getEntryClass().getName()
575 + "'");
576 } else if (complexAttributeMap.containsKey(collectionName)) {
577 throw new DuplicateEntryException("collection '"
578 + collectionName
579 + "' already defined as Complex Attribute for class '"
580 + getEntryClass().getName()
581 + "'");
582 }
583
584 collectionMap.put(collectionName, collection);
585
586 }
587 this.collections = collections;
588 }
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620 public void setRelationships(List<RelationshipDefinition> relationships) {
621 this.relationships = relationships;
622 }
623
624 public Set<String> getCollectionNames() {
625 return collectionMap.keySet();
626 }
627
628 public Set<String> getAttributeNames() {
629 return attributeMap.keySet();
630 }
631
632 public Set<String> getRelationshipNames() {
633 return relationshipMap.keySet();
634 }
635
636
637
638
639
640
641
642 private void addNestedAttributes(ComplexAttributeDefinition complexAttribute, String attrPath) {
643 DataDictionaryEntryBase dataDictionaryEntry = (DataDictionaryEntryBase) complexAttribute.getDataObjectEntry();
644
645
646 for (AttributeDefinition attribute : dataDictionaryEntry.getAttributes()) {
647 String nestedAttributeName = attrPath + "." + attribute.getName();
648 AttributeDefinition nestedAttribute = copyAttributeDefinition(attribute);
649 nestedAttribute.setName(nestedAttributeName);
650
651 if (!attributeMap.containsKey(nestedAttributeName)) {
652 this.attributes.add(nestedAttribute);
653 this.attributeMap.put(nestedAttributeName, nestedAttribute);
654 }
655 }
656
657
658 List<ComplexAttributeDefinition> nestedComplexAttributes = dataDictionaryEntry.getComplexAttributes();
659 if (nestedComplexAttributes != null) {
660 for (ComplexAttributeDefinition nestedComplexAttribute : nestedComplexAttributes) {
661 addNestedAttributes(nestedComplexAttribute, attrPath + "." + nestedComplexAttribute.getName());
662 }
663 }
664 }
665
666
667
668
669
670
671
672 private AttributeDefinition copyAttributeDefinition(AttributeDefinition attrDefToCopy) {
673 AttributeDefinition attrDefCopy = new AttributeDefinition();
674
675 try {
676 BeanUtils.copyProperties(attrDefToCopy, attrDefCopy, new String[]{"formatterClass"});
677
678
679 attrDefCopy.setRequired(attrDefToCopy.isRequired());
680
681 } catch (Exception e) {
682 LOG.warn( "Problem copying properties from attribute definition: " + attrDefToCopy, e);
683 }
684
685 return attrDefCopy;
686 }
687
688
689
690
691 @Override
692 @BeanTagAttribute(name = "stateMapping", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
693 public StateMapping getStateMapping() {
694 return stateMapping;
695 }
696
697
698
699
700 @Override
701 public void setStateMapping(StateMapping stateMapping) {
702 this.stateMapping = stateMapping;
703 }
704
705 public boolean hasEmbeddedDataObjectMetadata() {
706 return getDataObjectMetadata() != null;
707 }
708
709 public DataObjectMetadata getDataObjectMetadata() {
710 return dataObjectMetadata;
711 }
712
713 public void setDataObjectMetadata(DataObjectMetadata dataObjectMetadata) {
714 this.dataObjectMetadata = dataObjectMetadata;
715 }
716
717 public Map<String, RelationshipDefinition> getRelationshipMap() {
718 if(relationshipMap.isEmpty() && !getRelationships().isEmpty()){
719 for(RelationshipDefinition rel : getRelationships()){
720 relationshipMap.put(rel.getObjectAttributeName(),rel);
721 }
722 }
723 return relationshipMap;
724 }
725
726 public void setRelationshipMap(Map<String, RelationshipDefinition> relationshipMap) {
727 this.relationshipMap = relationshipMap;
728 }
729 }