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 for (RelationshipDefinition relationship : relationships) {
375 if (relationship == null) {
376 LOG.warn("Skipping invalid (null) relationshipDefinition on " + this);
377 continue;
378 }
379 String relationshipName = relationship.getObjectAttributeName();
380 if (StringUtils.isBlank(relationshipName)) {
381 LOG.warn("Skipping invalid relationshipDefinition with blank relationshipName on " + this);
382 continue;
383 }
384 relationship.setSourceClass(getEntryClass());
385 relationshipMap.put(relationshipName, relationship);
386 }
387 }
388
389
390 if (complexAttributes != null) {
391 for (ComplexAttributeDefinition complexAttribute : complexAttributes) {
392 if ( complexAttribute != null ) {
393 addNestedAttributes(complexAttribute, complexAttribute.getName());
394 }
395 }
396 }
397 for (AttributeDefinition definition : getAttributes()) {
398 definition.dataDictionaryPostProcessing();
399 }
400 for (CollectionDefinition definition : getCollections()) {
401 definition.dataDictionaryPostProcessing();
402 }
403 for (RelationshipDefinition definition : getRelationships()) {
404 definition.dataDictionaryPostProcessing();
405 }
406 }
407
408
409
410
411
412
413
414 @Override
415 public void completeValidation(ValidationTrace tracer) {
416 if ( getEntryClass() != null ) {
417 if ( LOG.isDebugEnabled() ) {
418 LOG.debug( "Processing Validation for " + this.getClass().getSimpleName() + " for class: " + getEntryClass().getName() );
419 }
420 tracer.addBean(this.getClass().getSimpleName(), getEntryClass().getSimpleName());
421 for (AttributeDefinition definition : getAttributes()) {
422 definition.completeValidation(getEntryClass(), null, tracer.getCopy());
423 }
424 for (CollectionDefinition definition : getCollections()) {
425 definition.completeValidation(getEntryClass(), null, tracer.getCopy());
426 }
427 for (RelationshipDefinition definition : getRelationships()) {
428 definition.completeValidation(getEntryClass(), null, tracer.getCopy());
429 }
430 } else {
431 tracer.addBean(this.getClass().getSimpleName(), toString() );
432 tracer.createError("DataObjectClass is not set, remaining validations aborted", null );
433 }
434 }
435
436
437
438
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 public void setAttributes(List<AttributeDefinition> attributes) {
485 attributeMap.clear();
486 for (AttributeDefinition attribute : attributes) {
487 if (attribute == null) {
488 throw new IllegalArgumentException("invalid (null) attributeDefinition");
489 }
490 String attributeName = attribute.getName();
491 if (StringUtils.isBlank(attributeName)) {
492 throw new ValidationException("invalid (blank) attributeName");
493 }
494
495 if (attributeMap.containsKey(attributeName)) {
496 throw new DuplicateEntryException("attribute '"
497 + attributeName
498 + "' already defined as an Attribute for class '"
499 + getEntryClass().getName()
500 + "'");
501 } else if (collectionMap.containsKey(attributeName)) {
502 throw new DuplicateEntryException("attribute '"
503 + attributeName
504 + "' already defined as a Collection for class '"
505 + getEntryClass().getName()
506 + "'");
507 } else if (complexAttributeMap.containsKey(attributeName)) {
508 throw new DuplicateEntryException("attribute '"
509 + attributeName
510 + "' already defined as an Complex Attribute for class '"
511 + getEntryClass().getName()
512 + "'");
513 }
514 attributeMap.put(attributeName, attribute);
515 }
516 this.attributes = attributes;
517 }
518
519
520
521
522
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 public void setCollections(List<CollectionDefinition> collections) {
548 collectionMap.clear();
549 for (CollectionDefinition collection : collections) {
550 if (collection == null) {
551 throw new IllegalArgumentException("invalid (null) collectionDefinition");
552 }
553 String collectionName = collection.getName();
554 if (StringUtils.isBlank(collectionName)) {
555 throw new ValidationException("invalid (blank) collectionName");
556 }
557
558 if (collectionMap.containsKey(collectionName)) {
559 throw new DuplicateEntryException("collection '"
560 + collectionName
561 + "' already defined for class '"
562 + getEntryClass().getName()
563 + "'");
564 } else if (attributeMap.containsKey(collectionName)) {
565 throw new DuplicateEntryException("collection '"
566 + collectionName
567 + "' already defined as an Attribute for class '"
568 + getEntryClass().getName()
569 + "'");
570 } else if (complexAttributeMap.containsKey(collectionName)) {
571 throw new DuplicateEntryException("collection '"
572 + collectionName
573 + "' already defined as Complex Attribute for class '"
574 + getEntryClass().getName()
575 + "'");
576 }
577
578 collectionMap.put(collectionName, collection);
579
580 }
581 this.collections = collections;
582 }
583
584
585
586
587
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 public void setRelationships(List<RelationshipDefinition> relationships) {
615 this.relationships = relationships;
616 }
617
618 public Set<String> getCollectionNames() {
619 return collectionMap.keySet();
620 }
621
622 public Set<String> getAttributeNames() {
623 return attributeMap.keySet();
624 }
625
626 public Set<String> getRelationshipNames() {
627 return relationshipMap.keySet();
628 }
629
630
631
632
633
634
635
636 private void addNestedAttributes(ComplexAttributeDefinition complexAttribute, String attrPath) {
637 DataDictionaryEntryBase dataDictionaryEntry = (DataDictionaryEntryBase) complexAttribute.getDataObjectEntry();
638
639
640 for (AttributeDefinition attribute : dataDictionaryEntry.getAttributes()) {
641 String nestedAttributeName = attrPath + "." + attribute.getName();
642 AttributeDefinition nestedAttribute = copyAttributeDefinition(attribute);
643 nestedAttribute.setName(nestedAttributeName);
644
645 if (!attributeMap.containsKey(nestedAttributeName)) {
646 this.attributes.add(nestedAttribute);
647 this.attributeMap.put(nestedAttributeName, nestedAttribute);
648 }
649 }
650
651
652 List<ComplexAttributeDefinition> nestedComplexAttributes = dataDictionaryEntry.getComplexAttributes();
653 if (nestedComplexAttributes != null) {
654 for (ComplexAttributeDefinition nestedComplexAttribute : nestedComplexAttributes) {
655 addNestedAttributes(nestedComplexAttribute, attrPath + "." + nestedComplexAttribute.getName());
656 }
657 }
658 }
659
660
661
662
663
664
665
666 private AttributeDefinition copyAttributeDefinition(AttributeDefinition attrDefToCopy) {
667 AttributeDefinition attrDefCopy = new AttributeDefinition();
668
669 try {
670 BeanUtils.copyProperties(attrDefToCopy, attrDefCopy, new String[]{"formatterClass"});
671
672
673 attrDefCopy.setRequired(attrDefToCopy.isRequired());
674
675 } catch (Exception e) {
676 LOG.warn( "Problem copying properties from attribute definition: " + attrDefToCopy, e);
677 }
678
679 return attrDefCopy;
680 }
681
682
683
684
685 @Override
686 @BeanTagAttribute(name = "stateMapping", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
687 public StateMapping getStateMapping() {
688 return stateMapping;
689 }
690
691
692
693
694 @Override
695 public void setStateMapping(StateMapping stateMapping) {
696 this.stateMapping = stateMapping;
697 }
698
699 public boolean hasEmbeddedDataObjectMetadata() {
700 return getDataObjectMetadata() != null;
701 }
702
703 public DataObjectMetadata getDataObjectMetadata() {
704 return dataObjectMetadata;
705 }
706
707 public void setDataObjectMetadata(DataObjectMetadata dataObjectMetadata) {
708 this.dataObjectMetadata = dataObjectMetadata;
709 }
710
711 public Map<String, RelationshipDefinition> getRelationshipMap() {
712 if(relationshipMap.isEmpty() && !getRelationships().isEmpty()){
713 for(RelationshipDefinition rel : getRelationships()){
714 relationshipMap.put(rel.getObjectAttributeName(),rel);
715 }
716 }
717 return relationshipMap;
718 }
719
720 public void setRelationshipMap(Map<String, RelationshipDefinition> relationshipMap) {
721 this.relationshipMap = relationshipMap;
722 }
723 }