1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.rice.krad.service.impl;
17  
18  import java.beans.PropertyDescriptor;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.commons.beanutils.PropertyUtils;
28  import org.apache.commons.lang.StringUtils;
29  import org.kuali.rice.core.framework.persistence.jpa.metadata.EntityDescriptor;
30  import org.kuali.rice.core.framework.persistence.jpa.metadata.JoinColumnDescriptor;
31  import org.kuali.rice.core.framework.persistence.jpa.metadata.MetadataManager;
32  import org.kuali.rice.core.framework.persistence.jpa.metadata.ObjectDescriptor;
33  import org.kuali.rice.krad.bo.DataObjectRelationship;
34  import org.kuali.rice.krad.bo.PersistableBusinessObject;
35  import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException;
36  import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException;
37  import org.kuali.rice.krad.exception.ReferenceAttributeNotAnOjbReferenceException;
38  import org.kuali.rice.krad.service.PersistenceStructureService;
39  import org.kuali.rice.krad.util.ForeignKeyFieldsPopulationState;
40  
41  public class PersistenceStructureServiceJpaImpl extends PersistenceServiceImplBase implements PersistenceStructureService {
42  
43  	
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  	public static Map<Class, Class> referenceConversionMap = new HashMap<Class, Class>();
59  
60  	
61  
62  
63  	
64  	public boolean isPersistable(Class clazz) {
65  		boolean isPersistable = false;
66  
67  		if (MetadataManager.getEntityDescriptor(clazz) != null) {
68  			isPersistable = true;
69  		}
70  
71  		return isPersistable;
72  	}
73  
74  	
75  
76  
77  	
78  	public List getPrimaryKeys(Class clazz) {
79  		List pkList = new ArrayList();
80  
81  		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(clazz);
82  		for (org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor field : descriptor.getPrimaryKeys()) {
83  			pkList.add(field.getName());
84  		}
85  
86  		return pkList;
87  	}
88  
89  	
90  
91  
92  	
93  	public List listFieldNames(Class clazz) {
94  		List fieldNames = new ArrayList();
95  
96  		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(clazz);
97  		for (org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor field : descriptor.getFields()) {
98  			fieldNames.add(field.getName());
99  		}
100 
101 		return fieldNames;
102 	}
103 
104 	
105 
106 
107 	
108 	
109 	public Object clearPrimaryKeyFields(Object persistableObject) {
110 		
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 		return persistableObject;
132 	}
133 
134 	
135 
136 
137 	
138 	
139 	
140 	public List listPersistableSubclasses(Class superclazz) {
141 		List persistableSubclasses = new ArrayList();
142 		
143 
144 
145 
146 
147 
148 
149 
150 
151 
152 
153 
154 
155 
156 		return persistableSubclasses;
157 	}
158 
159 	
160 
161 
162 
163 	
164 	public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName, String attributePrefix) {
165 		if (persistableClass == null) {
166 			throw new IllegalArgumentException("invalid (null) persistableClass");
167 		}
168 		if (StringUtils.isBlank(attributeName)) {
169 			throw new IllegalArgumentException("invalid (blank) attributeName");
170 		}
171 
172 		Map<String, DataObjectRelationship> relationships = new HashMap<String, DataObjectRelationship>();
173 
174 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableClass);
175 		for (ObjectDescriptor objectDescriptor : descriptor.getObjectRelationships()) {
176 			List<String> fks = objectDescriptor.getForeignKeyFields();
177 			if (fks.contains(attributeName) || objectDescriptor.getAttributeName().equals(attributeName)) {
178 				Map<String, String> fkToPkRefs = getForeignKeysForReference(persistableClass, objectDescriptor.getAttributeName());
179 				DataObjectRelationship
180                         rel = new DataObjectRelationship(persistableClass, objectDescriptor.getAttributeName(), objectDescriptor.getTargetEntity());
181 				for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) {
182 					if (StringUtils.isBlank(attributePrefix)) {
183 						rel.getParentToChildReferences().put(ref.getKey(), ref.getValue());
184 					} else {
185 						rel.getParentToChildReferences().put(attributePrefix + "." + ref.getKey(), ref.getValue());
186 					}
187 				}
188 				relationships.put(objectDescriptor.getAttributeName(), rel);
189 			}
190 		}
191 
192 		return relationships;
193 	}
194 
195 	
196 	
197 	public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName) {
198 		return getRelationshipMetadata(persistableClass, attributeName, null);
199 	}
200 
201 	
202 
203 
204 
205 	
206 	public String getForeignKeyFieldName(Class persistableObjectClass, String attributeName, String pkName) {
207 		String fkName = null;
208 
209 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableObjectClass);
210 		ObjectDescriptor objectDescriptor = descriptor.getObjectDescriptorByName(attributeName);
211 		if (objectDescriptor == null) {
212 			throw new RuntimeException("Attribute name " + attributeName + " is not a valid reference to class " + persistableObjectClass.getName());
213 		}
214 		List<org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor> matches = new ArrayList<org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor>();
215 		for (org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor field : descriptor.getFields()) {
216 			String column = field.getColumn();
217 			for (JoinColumnDescriptor join : objectDescriptor.getJoinColumnDescriptors()) {
218 				if (column != null && column.equals(join.getName())) {
219 					matches.add(field);
220 				}
221 			}
222 		}
223 
224 		if (matches.size() == 1) {
225 			fkName = matches.get(0).getName();
226 		} else {
227 			throw new RuntimeException("Implement me!");
228 		}
229 
230 		return fkName;
231 	}
232 
233 	
234 
235 
236 
237 	
238 	public Map getReferencesForForeignKey(Class persistableObjectClass, String attributeName) {
239 		Map referenceClasses = new HashMap();
240 
241 		if (PersistableBusinessObject.class.isAssignableFrom(persistableObjectClass)) {
242 			EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableObjectClass);
243 			for (ObjectDescriptor objectDescriptor : descriptor.getObjectRelationships()) {
244 				List<String> refFkNames = objectDescriptor.getForeignKeyFields();
245 				for (String fk : refFkNames) {
246 					if (fk.equals(attributeName)) {
247 						referenceClasses.put(objectDescriptor.getAttributeName(), objectDescriptor.getTargetEntity());
248 					}
249 				}
250 			}
251 		}
252 
253 		return referenceClasses;
254 	}
255 
256 	
257 
258 
259 
260 
261 
262 
263 
264 
265 
266 
267 
268 
269 
270 
271 
272 	
273 	public Map getForeignKeysForReference(Class clazz, String attributeName) {
274 
275 		
276 		if (clazz == null) {
277 			throw new IllegalArgumentException("The Class passed in for the clazz argument was null.");
278 		}
279 		if (attributeName == null) {
280 			throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
281 		}
282 
283 		
284 		Class attributeClass = getBusinessObjectAttributeClass(clazz, attributeName);
285 		if (attributeClass == null) {
286 			throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + attributeName + "' does not exist on class: '" + clazz.getName() + "'.");
287 		}
288 
289 		
290 		
291 		if (!PersistableBusinessObject.class.isAssignableFrom(attributeClass)) {
292 			throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + attributeName + ") is of class: '" + attributeClass.getName() + "' and is not a descendent of BusinessObject.  Only descendents of BusinessObject can be used.");
293 		}
294 
295 		return determineFkMap(clazz, attributeName, attributeClass);
296 	}
297 
298 	private Map determineFkMap(Class clazz, String attributeName, Class attributeClass) {
299 		Map fkMap = new HashMap();
300 		EntityDescriptor entityDescriptor = MetadataManager.getEntityDescriptor(clazz);
301 		ObjectDescriptor objectDescriptor = entityDescriptor.getObjectDescriptorByName(attributeName);
302 		if (objectDescriptor == null) {
303 			throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + attributeName + ") is not defined in JPA annotations for class: '" + clazz.getName() + "'");
304 		}
305 
306 		
307 		
308 		
309 		
310 		
311 		if (!attributeClass.equals(objectDescriptor.getTargetEntity())) {
312 			if (referenceConversionMap.containsKey(attributeClass)) {
313 				attributeClass = referenceConversionMap.get(attributeClass);
314 			} else {
315 				throw new RuntimeException("The Class of the Java member [" + attributeClass.getName() + "] '" + attributeName + "' does not match the class of the reference [" + objectDescriptor.getTargetEntity().getName() + "]. " + "This is an unhandled special case for which special code needs to be written in this class.");
316 			}
317 		}
318 
319 		
320 		
321 		List<String> fkFields = objectDescriptor.getForeignKeyFields();
322 		Iterator<String> fkIterator = fkFields.iterator();
323 
324 		
325 		
326 		List pkFields = getPrimaryKeys(attributeClass);
327 		Iterator pkIterator = pkFields.iterator();
328 
329 		
330 		
331 		if (pkFields.size() != fkFields.size()) {
332 			throw new RuntimeException("KualiPersistenceStructureService Error: The number of " + "foreign keys doesnt match the number of primary keys.");
333 		}
334 
335 		
336 		while (fkIterator.hasNext()) {
337 			
338 			
339 			
340 			if (!pkIterator.hasNext()) {
341 				throw new RuntimeException("The number of foriegn keys dont match the number of primary keys for the reference '" + attributeName + "', on BO of type '" + clazz.getName() + "'.  " + "This should never happen under normal circumstances.");
342 			}
343 
344 			
345 			String fkFieldName = (String) fkIterator.next();
346 			String pkFieldName = (String) pkIterator.next();
347 
348 			
349 			fkMap.put(fkFieldName, pkFieldName);
350 		}
351 		return fkMap;
352 	}
353 
354 	
355 	public Map<String, String> getInverseForeignKeysForCollection(Class boClass, String collectionName) {
356 		
357 		if (boClass == null) {
358 			throw new IllegalArgumentException("The Class passed in for the boClass argument was null.");
359 		}
360 		if (collectionName == null) {
361 			throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
362 		}
363 
364 		PropertyDescriptor propertyDescriptor = null;
365 
366 		
367 		Object classInstance;
368 		try {
369 			classInstance = boClass.newInstance();
370 		} catch (Exception e) {
371 			throw new RuntimeException(e);
372 		}
373 
374 		
375 		try {
376 			propertyDescriptor = PropertyUtils.getPropertyDescriptor(classInstance, collectionName);
377 		} catch (Exception e) {
378 			throw new RuntimeException(e);
379 		}
380 		if (propertyDescriptor == null) {
381 			throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + collectionName + "' does not exist " + "on class: '" + boClass.getName() + "'. GFK");
382 		}
383 
384 		
385 		Class attributeClass = propertyDescriptor.getPropertyType();
386 
387 		
388 		
389 		if (!Collection.class.isAssignableFrom(attributeClass)) {
390 			throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + collectionName + ") is of class: " + "'" + attributeClass.getName() + "' and is not a " + "descendent of Collection");
391 		}
392 
393 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
394 		org.kuali.rice.core.framework.persistence.jpa.metadata.CollectionDescriptor cd = descriptor.getCollectionDescriptorByName(collectionName);
395 		List<String> childPrimaryKeys = cd.getForeignKeyFields();
396 
397 		List parentForeignKeys = getPrimaryKeys(boClass);
398 
399 		if (parentForeignKeys.size() != childPrimaryKeys.size()) {
400 			throw new RuntimeException("The number of keys in the class descriptor and the inverse foreign key mapping for the collection descriptors do not match.");
401 		}
402 
403 		Map<String, String> fkToPkMap = new HashMap<String, String>();
404 		Iterator pFKIter = parentForeignKeys.iterator();
405 		Iterator cPKIterator = childPrimaryKeys.iterator();
406 
407 		while (pFKIter.hasNext()) {
408 			String parentForeignKey = (String) pFKIter.next();
409 			String childPrimaryKey = (String) cPKIterator.next();
410 
411 			fkToPkMap.put(parentForeignKey, childPrimaryKey);
412 		}
413 		return fkToPkMap;
414 	}
415 
416 	
417 
418 
419 	
420 	public Map getNestedForeignKeyMap(Class persistableObjectClass) {
421 		Map fkMap = new HashMap();
422 
423 		
424 		
425 		
426 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableObjectClass);
427 		for (ObjectDescriptor objectReferenceDescriptor : descriptor.getObjectRelationships()) {
428 			EntityDescriptor referenceDescriptor = MetadataManager.getEntityDescriptor(objectReferenceDescriptor.getTargetEntity());
429 			List<String> fkFields = objectReferenceDescriptor.getForeignKeyFields();
430 			Set<org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor> pkFields = referenceDescriptor.getPrimaryKeys();
431 			int i = 0;
432 			for (org.kuali.rice.core.framework.persistence.jpa.metadata.FieldDescriptor fd : pkFields) {
433 				fkMap.put(objectReferenceDescriptor.getAttributeName() + "." + fd.getName(), fkFields.get(i));
434 				i++;
435 			}
436 		}
437 
438 		return fkMap;
439 	}
440 
441 	
442 
443 
444 	public boolean hasPrimaryKeyFieldValues(Object persistableObject) {
445 		Map keyFields = getPrimaryKeyFieldValues(persistableObject);
446 
447 		boolean emptyField = false;
448 		for (Iterator i = keyFields.entrySet().iterator(); !emptyField && i.hasNext();) {
449 			Map.Entry e = (Map.Entry) i.next();
450 
451 			Object fieldValue = e.getValue();
452 			if (fieldValue == null) {
453 				emptyField = true;
454 			} else if (fieldValue instanceof String) {
455 				if (StringUtils.isEmpty((String) fieldValue)) {
456 					emptyField = true;
457 				} else {
458 					emptyField = false;
459 				}
460 			}
461 		}
462 
463 		return !emptyField;
464 	}
465 
466 	
467 
468 
469 
470 	public ForeignKeyFieldsPopulationState getForeignKeyFieldsPopulationState(PersistableBusinessObject bo, String referenceName) {
471 
472 		boolean allFieldsPopulated = true;
473 		boolean anyFieldsPopulated = false;
474 		List<String> unpopulatedFields = new ArrayList<String>();
475 
476 		
477 		if (bo == null) {
478 			throw new IllegalArgumentException("The Class passed in for the BusinessObject argument was null.");
479 		}
480 		if (StringUtils.isBlank(referenceName)) {
481 			throw new IllegalArgumentException("The String passed in for the referenceName argument was null or empty.");
482 		}
483 
484 		PropertyDescriptor propertyDescriptor = null;
485 
486 		
487 		try {
488 			propertyDescriptor = PropertyUtils.getPropertyDescriptor(bo, referenceName);
489 		} catch (Exception e) {
490 			throw new RuntimeException(e);
491 		}
492 		if (propertyDescriptor == null) {
493 			throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + referenceName + "' does not exist " + "on class: '" + bo.getClass().getName() + "'.");
494 		}
495 
496 		
497 		Class referenceClass = propertyDescriptor.getPropertyType();
498 
499 		
500 		
501 		if (!PersistableBusinessObject.class.isAssignableFrom(referenceClass)) {
502 			throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + referenceName + ") is of class: " + "'" + referenceClass.getName() + "' and is not a " + "descendent of BusinessObject.  Only descendents of BusinessObject " + "can be used.");
503 		}
504 
505 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(bo.getClass());
506 		ObjectDescriptor objectDescriptor = descriptor.getObjectDescriptorByName(referenceName);
507 
508 		if (objectDescriptor == null) {
509 			throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + referenceName + ") is not listed " + "in OJB as a reference-descriptor for class: '" + bo.getClass().getName() + "'");
510 		}
511 
512 		List<String> fkFields = objectDescriptor.getForeignKeyFields();
513 		Iterator fkIterator = fkFields.iterator();
514 
515 		
516 		while (fkIterator.hasNext()) {
517 
518 			
519 			String fkFieldName = (String) fkIterator.next();
520 
521 			
522 			Object fkFieldValue = null;
523 			try {
524 				fkFieldValue = PropertyUtils.getSimpleProperty(bo, fkFieldName);
525 			}
526 
527 			
528 			catch (Exception e) {
529 				throw new RuntimeException(e);
530 			}
531 
532 			
533 			if (fkFieldValue == null) {
534 				allFieldsPopulated = false;
535 				unpopulatedFields.add(fkFieldName);
536 			} else if (fkFieldValue instanceof String) {
537 				if (StringUtils.isBlank((String) fkFieldValue)) {
538 					allFieldsPopulated = false;
539 					unpopulatedFields.add(fkFieldName);
540 				} else {
541 					anyFieldsPopulated = true;
542 				}
543 			} else {
544 				anyFieldsPopulated = true;
545 			}
546 		}
547 
548 		
549 		
550 		if (allFieldsPopulated) {
551 			if (!unpopulatedFields.isEmpty()) {
552 				throw new RuntimeException("The flag is set that indicates all fields are populated, but there " + "are fields present in the unpopulatedFields list.  This should never happen, and indicates " + "that the logic in this method is broken.");
553 			}
554 		}
555 
556 		return new ForeignKeyFieldsPopulationState(allFieldsPopulated, anyFieldsPopulated, unpopulatedFields);
557 	}
558 
559 	
560 
561 
562 	
563 	public Map<String, Class> listReferenceObjectFields(Class boClass) {
564 		
565 		if (boClass == null) {
566 			throw new IllegalArgumentException("Class specified in the parameter was null.");
567 		}
568 		if (!PersistableBusinessObject.class.isAssignableFrom(boClass)) {
569 			throw new IllegalArgumentException("Class specified [" + boClass.getName() + "] must be a class that " + "inherits from BusinessObject.");
570 		}
571 
572 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
573 		Map<String, Class> references = new HashMap();
574 		for (org.kuali.rice.core.framework.persistence.jpa.metadata.ObjectDescriptor od : descriptor.getObjectRelationships()) {
575 			references.put(od.getAttributeName(), od.getTargetEntity());
576 		}
577 
578 		return references;
579 	}
580 
581 	
582 	public Map<String, Class> listCollectionObjectTypes(Class boClass) {
583 		if (boClass == null) {
584 			throw new IllegalArgumentException("Class specified in the parameter was null.");
585 		}
586 
587 		Map<String, Class> references = new HashMap();
588 
589 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
590 		if (descriptor == null) {
591 			return references;
592 		}
593 
594 		for (org.kuali.rice.core.framework.persistence.jpa.metadata.CollectionDescriptor cd : descriptor.getCollectionRelationships()) {
595 			references.put(cd.getAttributeName(), cd.getTargetEntity());
596 		}
597 
598 		return references;
599 	}
600 
601 	public Map<String, Class> listCollectionObjectTypes(PersistableBusinessObject bo) {
602 		
603 		if (bo == null) {
604 			throw new IllegalArgumentException("BO specified in the parameter was null.");
605 		}
606 		if (!(bo instanceof PersistableBusinessObject)) {
607 			throw new IllegalArgumentException("BO specified [" + bo.getClass().getName() + "] must be a class that " + "inherits from BusinessObject.");
608 		}
609 
610 		return listCollectionObjectTypes(bo.getClass());
611 	}
612 
613 	
614 
615 
616 	public Map<String, Class> listReferenceObjectFields(PersistableBusinessObject bo) {
617 		
618 		if (bo == null) {
619 			throw new IllegalArgumentException("BO specified in the parameter was null.");
620 		}
621 		if (!(bo instanceof PersistableBusinessObject)) {
622 			throw new IllegalArgumentException("BO specified [" + bo.getClass().getName() + "] must be a class that " + "inherits from BusinessObject.");
623 		}
624 
625 		return listReferenceObjectFields(bo.getClass());
626 	}
627 
628 	
629 	public boolean isReferenceUpdatable(Class boClass, String referenceName) {
630 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
631 		return descriptor.getObjectDescriptorByName(referenceName).isUpdateable();
632 
633 
634 
635 
636 
637 
638 	}
639 
640 	
641 	public boolean isCollectionUpdatable(Class boClass, String collectionName) {
642 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
643 		return descriptor.getCollectionDescriptorByName(collectionName).isUpdateable();
644 
645 
646 
647 
648 
649 
650 	}
651 
652 	
653 	public boolean hasCollection(Class boClass, String collectionName) {
654 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
655 		return descriptor.getCollectionDescriptorByName(collectionName) != null;
656 	}
657 
658 	
659 	public boolean hasReference(Class boClass, String referenceName) {
660 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
661 		return descriptor.getObjectDescriptorByName(referenceName) != null;
662 	}
663 
664 	
665 
666 
667 	
668 	public String getTableName(Class<? extends PersistableBusinessObject> boClass) {
669 		EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(boClass);
670 		return descriptor.getTable();
671 	}
672 }
673