001: /*
002: * Copyright 2006-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.core.service.impl;
017:
018: import java.beans.PropertyDescriptor;
019: import java.lang.reflect.InvocationTargetException;
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.Vector;
027:
028: import org.apache.commons.beanutils.PropertyUtils;
029: import org.apache.commons.lang.StringUtils;
030: import org.apache.ojb.broker.metadata.ClassDescriptor;
031: import org.apache.ojb.broker.metadata.CollectionDescriptor;
032: import org.apache.ojb.broker.metadata.FieldDescriptor;
033: import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
034: import org.kuali.core.bo.BusinessObjectRelationship;
035: import org.kuali.core.bo.PersistableBusinessObject;
036: import org.kuali.core.bo.user.UniversalUser;
037: import org.kuali.core.exceptions.ClassNotPersistableException;
038: import org.kuali.core.exceptions.IntrospectionException;
039: import org.kuali.core.exceptions.ObjectNotABusinessObjectRuntimeException;
040: import org.kuali.core.exceptions.ReferenceAttributeDoesntExistException;
041: import org.kuali.core.exceptions.ReferenceAttributeNotAnOjbReferenceException;
042: import org.kuali.core.service.PersistenceStructureService;
043: import org.kuali.core.util.ForeignKeyFieldsPopulationState;
044: import org.kuali.core.util.spring.Cached;
045:
046: public class PersistenceStructureServiceImpl extends
047: PersistenceServiceImplBase implements
048: PersistenceStructureService {
049:
050: /**
051: *
052: * special case when the attributeClass passed in doesnt match the
053: * class of the reference-descriptor as defined in ojb-repository. Currently
054: * the only case of this happening is ObjectCode vs. ObjectCodeCurrent.
055: *
056: * NOTE: This method makes no real sense and is a product of a hack introduced by KFS for
057: * an unknown reason. If you find yourself using this map stop and go do something else.
058: *
059: * @param from the class in the code
060: * @param to the class in the repository
061: */
062: public static Map<Class, Class> referenceConversionMap = new HashMap<Class, Class>();
063:
064: /**
065: * @see org.kuali.core.service.PersistenceService#isPersistable(java.lang.Class)
066: */
067: @Cached
068: public boolean isPersistable(Class clazz) {
069: boolean isPersistable = false;
070:
071: try {
072: if (getClassDescriptor(clazz) != null) {
073: isPersistable = true;
074: }
075: } catch (ClassNotPersistableException e) {
076: isPersistable = false;
077: }
078:
079: return isPersistable;
080: }
081:
082: /**
083: * @see org.kuali.core.service.PersistenceService#getPrimaryKeyFieldAnonymousMarking(java.lang.Class)
084: */
085: @Cached
086: public Map getPrimaryKeyFieldAnonymousMarking(Class clazz) {
087: ClassDescriptor classDescriptor = getClassDescriptor(clazz);
088:
089: Map pkAnonyMap = new HashMap();
090: FieldDescriptor keyDescriptors[] = classDescriptor
091: .getPkFields();
092: for (int i = 0; i < keyDescriptors.length; ++i) {
093: FieldDescriptor keyDescriptor = keyDescriptors[i];
094: pkAnonyMap.put(keyDescriptor.getAttributeName(), Boolean
095: .valueOf(keyDescriptor.isAnonymous()));
096: }
097:
098: return pkAnonyMap;
099: }
100:
101: /**
102: * @see org.kuali.core.service.PersistenceService#getPrimaryKeys(java.lang.Class)
103: */
104: @Cached
105: public List getPrimaryKeys(Class clazz) {
106: ClassDescriptor classDescriptor = getClassDescriptor(clazz);
107:
108: List pkList = new ArrayList();
109: FieldDescriptor keyDescriptors[] = classDescriptor
110: .getPkFields();
111: for (int i = 0; i < keyDescriptors.length; ++i) {
112: FieldDescriptor keyDescriptor = keyDescriptors[i];
113: pkList.add(keyDescriptor.getAttributeName());
114: }
115:
116: return pkList;
117: }
118:
119: /**
120: * @see org.kuali.core.service.PersistenceMetadataExplorerService#listFieldNames(java.lang.Class)
121: */
122: @Cached
123: public List listFieldNames(Class clazz) {
124: ClassDescriptor classDescriptor = getClassDescriptor(clazz);
125:
126: List fieldNames = new ArrayList();
127:
128: FieldDescriptor fieldDescriptors[] = classDescriptor
129: .getFieldDescriptions();
130:
131: for (int i = 0; i < fieldDescriptors.length; ++i) {
132: FieldDescriptor fieldDescriptor = fieldDescriptors[i];
133: fieldNames.add(fieldDescriptor.getAttributeName());
134: }
135:
136: return fieldNames;
137: }
138:
139: /**
140: * @see org.kuali.core.service.PersistenceMetadataService#clearPrimaryKeyFields(java.lang.Object)
141: */
142: public Object clearPrimaryKeyFields(Object persistableObject) {
143: if (persistableObject == null) {
144: throw new IllegalArgumentException(
145: "invalid (null) persistableObject");
146: }
147:
148: String className = null;
149: String fieldName = null;
150: try {
151: className = persistableObject.getClass().getName();
152: List fields = listPrimaryKeyFieldNames(persistableObject
153: .getClass());
154: for (Iterator i = fields.iterator(); i.hasNext();) {
155: fieldName = (String) i.next();
156:
157: PropertyUtils.setProperty(persistableObject, fieldName,
158: null);
159: }
160:
161: if (persistableObject instanceof PersistableBusinessObject) {
162: ((PersistableBusinessObject) persistableObject)
163: .setObjectId(null);
164: }
165: } catch (NoSuchMethodException e) {
166: throw new IntrospectionException("no setter for property '"
167: + className + "." + fieldName + "'", e);
168: } catch (IllegalAccessException e) {
169: throw new IntrospectionException(
170: "problem accessing property '" + className + "."
171: + fieldName + "'", e);
172: } catch (InvocationTargetException e) {
173: throw new IntrospectionException(
174: "problem invoking getter for property '"
175: + className + "." + fieldName + "'", e);
176: }
177:
178: return persistableObject;
179: }
180:
181: /**
182: * @see org.kuali.core.service.PersistenceMetadataExplorerService#listPersistableSubclasses(java.lang.Class)
183: */
184: @Cached
185: public List listPersistableSubclasses(Class super clazz) {
186: if (super clazz == null) {
187: throw new IllegalArgumentException(
188: "invalid (null) uberclass");
189: }
190:
191: Map allDescriptors = getDescriptorRepository()
192: .getDescriptorTable();
193: List persistableSubclasses = new ArrayList();
194: for (Iterator i = allDescriptors.entrySet().iterator(); i
195: .hasNext();) {
196: Map.Entry e = (Map.Entry) i.next();
197:
198: Class persistableClass = ((ClassDescriptor) e.getValue())
199: .getClassOfObject();
200: if (!super clazz.equals(persistableClass)
201: && super clazz.isAssignableFrom(persistableClass)) {
202: persistableSubclasses.add(persistableClass);
203: }
204: }
205: return persistableSubclasses;
206: }
207:
208: /**
209: * @see org.kuali.core.service.PersistenceService#getRelationshipMetadata(java.lang.Class, java.lang.String)
210: */
211: @Cached
212: public Map<String, BusinessObjectRelationship> getRelationshipMetadata(
213: Class persistableClass, String attributeName,
214: String attributePrefix) {
215: if (persistableClass == null) {
216: throw new IllegalArgumentException(
217: "invalid (null) persistableClass");
218: }
219: if (StringUtils.isBlank(attributeName)) {
220: throw new IllegalArgumentException(
221: "invalid (blank) attributeName");
222: }
223:
224: Map<String, BusinessObjectRelationship> relationships = new HashMap<String, BusinessObjectRelationship>();
225:
226: // find table and column for given class and attribute
227: ClassDescriptor classDescriptor = getClassDescriptor(persistableClass);
228: Vector<ObjectReferenceDescriptor> references = classDescriptor
229: .getObjectReferenceDescriptors();
230: for (ObjectReferenceDescriptor objRef : references) {
231: Vector fks = objRef.getForeignKeyFields();
232: if (fks.contains(attributeName)
233: || objRef.getAttributeName().equals(attributeName)) {
234: FieldDescriptor[] pkFields = getClassDescriptor(
235: objRef.getItemClass()).getPkFields();
236: Map<String, String> fkToPkRefs = getForeignKeysForReference(
237: persistableClass, objRef.getAttributeName());
238: BusinessObjectRelationship rel = new BusinessObjectRelationship(
239: persistableClass, objRef.getAttributeName(),
240: objRef.getItemClass());
241: for (Map.Entry<String, String> ref : fkToPkRefs
242: .entrySet()) {
243: if (StringUtils.isBlank(attributePrefix)) {
244: rel.getParentToChildReferences().put(
245: ref.getKey(), ref.getValue());
246: } else {
247: rel.getParentToChildReferences().put(
248: attributePrefix + "." + ref.getKey(),
249: ref.getValue());
250: }
251: }
252: relationships.put(objRef.getAttributeName(), rel);
253: }
254: }
255: return relationships;
256: }
257:
258: @Cached
259: public Map<String, BusinessObjectRelationship> getRelationshipMetadata(
260: Class persistableClass, String attributeName) {
261: return getRelationshipMetadata(persistableClass, attributeName,
262: null);
263: }
264:
265: /**
266: * @see org.kuali.core.service.PersistenceService#getForeignKeyFieldName(java.lang.Object, java.lang.String, java.lang.String)
267: */
268: @Cached
269: public String getForeignKeyFieldName(Class persistableObjectClass,
270: String attributeName, String pkName) {
271: String fkName = null;
272:
273: ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
274: ObjectReferenceDescriptor objectReferenceDescriptor = classDescriptor
275: .getObjectReferenceDescriptorByName(attributeName);
276: if (objectReferenceDescriptor == null) {
277: throw new RuntimeException("Attribute name "
278: + attributeName
279: + " is not a valid reference to class "
280: + persistableObjectClass.getName());
281: }
282: ClassDescriptor referenceDescriptor = this
283: .getClassDescriptor(objectReferenceDescriptor
284: .getItemClass());
285:
286: FieldDescriptor[] fkFields = objectReferenceDescriptor
287: .getForeignKeyFieldDescriptors(classDescriptor);
288: FieldDescriptor[] pkFields = referenceDescriptor.getPkFields();
289: for (int i = 0; i < pkFields.length; i++) {
290: FieldDescriptor pkField = pkFields[i];
291: if (pkField.getAttributeName().equals(pkName)) {
292: fkName = fkFields[i].getAttributeName();
293: }
294: }
295:
296: return fkName;
297: }
298:
299: /**
300: * @see org.kuali.core.service.PersistenceService#getReferencesForForeignKey(java.lang.Class, java.lang.String)
301: */
302: @Cached
303: public Map getReferencesForForeignKey(Class persistableObjectClass,
304: String attributeName) {
305: Map referenceClasses = new HashMap();
306:
307: if (PersistableBusinessObject.class
308: .isAssignableFrom(persistableObjectClass)) {
309: ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
310: Vector objectReferences = classDescriptor
311: .getObjectReferenceDescriptors();
312: for (Iterator iter = objectReferences.iterator(); iter
313: .hasNext();) {
314: ObjectReferenceDescriptor referenceDescriptor = (ObjectReferenceDescriptor) iter
315: .next();
316:
317: /*
318: * iterate through the fk keys for the reference object and if matches the attributeName add the class as a reference
319: */
320: FieldDescriptor[] refFkNames = referenceDescriptor
321: .getForeignKeyFieldDescriptors(classDescriptor);
322: for (int i = 0; i < refFkNames.length; i++) {
323: FieldDescriptor fkField = refFkNames[i];
324: if (fkField.getAttributeName()
325: .equals(attributeName)) {
326: referenceClasses.put(referenceDescriptor
327: .getAttributeName(),
328: referenceDescriptor.getItemClass());
329: }
330:
331: }
332: }
333: }
334:
335: return referenceClasses;
336: }
337:
338: /**
339: * @see org.kuali.core.service.PersistenceService#getForeignKeysForReference(java.lang.Class, java.lang.String) The Map
340: * structure is: Key(String fkFieldName) => Value(String pkFieldName) NOTE that this implementation depends on the ordering
341: * of foreign-key elements in the ojb-repository matching the ordering of primary-key declarations of the class on the
342: * other side of the relationship. This is done because: 1. The current version of OJB requires you to declare all of these
343: * things in the correct (and matching) order in the ojb-repository file for it to work at all. 2. There is no other way to
344: * match a given foreign-key reference to its corresponding primary-key on the opposing side of the relationship. Yes, this
345: * is a crummy way to do it, but OJB doesnt provide explicit matches of foreign-keys to primary keys, and always assumes
346: * that foreign-keys map to primary keys on the other object, and never to a set of candidate keys, or any other column.
347: */
348: @Cached
349: public Map getForeignKeysForReference(Class clazz,
350: String attributeName) {
351:
352: // yelp if nulls were passed in
353: if (clazz == null) {
354: throw new IllegalArgumentException(
355: "The Class passed in for the clazz argument was null.");
356: }
357: if (attributeName == null) {
358: throw new IllegalArgumentException(
359: "The String passed in for the attributeName argument was null.");
360: }
361:
362: Map fkMap = new HashMap();
363:
364: // get the class of the attribute name
365: Class attributeClass = getBusinessObjectAttributeClass(clazz,
366: attributeName);
367: if (attributeClass == null) {
368: throw new ReferenceAttributeDoesntExistException(
369: "Requested attribute: '" + attributeName
370: + "' does not exist " + "on class: '"
371: + clazz.getName() + "'.");
372: }
373:
374: // make sure the class of the attribute descends from BusinessObject,
375: // otherwise throw an exception
376: if (!PersistableBusinessObject.class
377: .isAssignableFrom(attributeClass)) {
378: throw new ObjectNotABusinessObjectRuntimeException(
379: "Attribute requested ("
380: + attributeName
381: + ") is of class: "
382: + "'"
383: + attributeClass.getName()
384: + "' and is not a "
385: + "descendent of BusinessObject. Only descendents of BusinessObject "
386: + "can be used.");
387: }
388:
389: // make sure the attribute designated is listed as a reference-descriptor
390: // on the clazz specified, otherwise throw an exception (OJB); UniversalUser objects
391: // will be excluded from this
392: if (!UniversalUser.class.equals(attributeClass)) {
393: ClassDescriptor classDescriptor = getClassDescriptor(clazz);
394: ObjectReferenceDescriptor referenceDescriptor = classDescriptor
395: .getObjectReferenceDescriptorByName(attributeName);
396: if (referenceDescriptor == null) {
397: throw new ReferenceAttributeNotAnOjbReferenceException(
398: "Attribute requested ("
399: + attributeName
400: + ") is not listed "
401: + "in OJB as a reference-descriptor for class: '"
402: + clazz.getName() + "'");
403: }
404:
405: // special case when the attributeClass passed in doesnt match the
406: // class of the reference-descriptor as defined in ojb-repository. Currently
407: // the only case of this happening is ObjectCode vs. ObjectCodeCurrent.
408: if (!attributeClass.equals(referenceDescriptor
409: .getItemClass())) {
410:
411: if (referenceConversionMap.containsKey(attributeClass)) {
412: attributeClass = referenceConversionMap
413: .get(attributeClass);
414: } else {
415: throw new RuntimeException(
416: "The Class of the Java member ["
417: + attributeClass.getName()
418: + "] '"
419: + attributeName
420: + "' does not match the class of the "
421: + "reference-descriptor ["
422: + referenceDescriptor
423: .getItemClass().getName()
424: + "]. "
425: + "This is an unhandled special case for which special code needs to be written "
426: + "in this class.");
427: }
428: }
429:
430: // get the list of the foreign-keys for this reference-descriptor (OJB)
431: Vector fkFields = referenceDescriptor.getForeignKeyFields();
432: Iterator fkIterator = fkFields.iterator();
433:
434: // get the list of the corresponding pk fields on the other side of the relationship
435: List pkFields = getPrimaryKeys(attributeClass);
436: Iterator pkIterator = pkFields.iterator();
437:
438: // make sure the size of the pkIterator is the same as the
439: // size of the fkIterator, otherwise this whole thing is borked
440: if (pkFields.size() != fkFields.size()) {
441: throw new RuntimeException(
442: "KualiPersistenceStructureService Error: The number of "
443: + "foreign keys doesnt match the number of primary keys. This may be a "
444: + "result of misconfigured OJB-repository files.");
445: }
446:
447: // walk through the list of the foreign keys, get their types
448: while (fkIterator.hasNext()) {
449:
450: // if there is a next FK but not a next PK, then we've got a big problem,
451: // and cannot continue
452: if (!pkIterator.hasNext()) {
453: throw new RuntimeException(
454: "The number of foriegn keys dont match the number of primary "
455: + "keys for the reference '"
456: + attributeName
457: + "', on BO of type '"
458: + clazz.getName()
459: + "'. "
460: + "This should never happen under normal circumstances, as it means that the OJB repository "
461: + "files are misconfigured.");
462: }
463:
464: // get the field name of the fk & pk field
465: String fkFieldName = (String) fkIterator.next();
466: String pkFieldName = (String) pkIterator.next();
467:
468: // add the fieldName and fieldType to the map
469: fkMap.put(fkFieldName, pkFieldName);
470:
471: }
472:
473: }
474:
475: return fkMap;
476: }
477:
478: @Cached
479: public Map<String, String> getInverseForeignKeysForCollection(
480: Class boClass, String collectionName) {
481: // yelp if nulls were passed in
482: if (boClass == null) {
483: throw new IllegalArgumentException(
484: "The Class passed in for the boClass argument was null.");
485: }
486: if (collectionName == null) {
487: throw new IllegalArgumentException(
488: "The String passed in for the attributeName argument was null.");
489: }
490:
491: Map fkMap = new HashMap();
492: PropertyDescriptor propertyDescriptor = null;
493:
494: // make an instance of the class passed
495: Object classInstance;
496: try {
497: classInstance = boClass.newInstance();
498: } catch (Exception e) {
499: throw new RuntimeException(e);
500: }
501:
502: // make sure the attribute exists at all, throw exception if not
503: try {
504: propertyDescriptor = PropertyUtils.getPropertyDescriptor(
505: classInstance, collectionName);
506: } catch (Exception e) {
507: throw new RuntimeException(e);
508: }
509: if (propertyDescriptor == null) {
510: throw new ReferenceAttributeDoesntExistException(
511: "Requested attribute: '" + collectionName
512: + "' does not exist " + "on class: '"
513: + boClass.getName() + "'. GFK");
514: }
515:
516: // get the class of the attribute name
517: Class attributeClass = propertyDescriptor.getPropertyType();
518:
519: // make sure the class of the attribute descends from BusinessObject,
520: // otherwise throw an exception
521: if (!Collection.class.isAssignableFrom(attributeClass)) {
522: throw new ObjectNotABusinessObjectRuntimeException(
523: "Attribute requested (" + collectionName
524: + ") is of class: " + "'"
525: + attributeClass.getName()
526: + "' and is not a "
527: + "descendent of Collection");
528: }
529:
530: // make sure the collection designated is listed as a collection-descriptor
531: // on the boClass specified, otherwise throw an exception
532: ClassDescriptor classDescriptor = getClassDescriptor(boClass);
533: CollectionDescriptor collectionDescriptor = classDescriptor
534: .getCollectionDescriptorByName(collectionName);
535:
536: // in collections, the number of keys is equal to the number of keys in the parent class (the class with the collection).
537: // Each of the primary keys on the parent object will be mapped to a field in the element object.
538:
539: List parentForeignKeys = getPrimaryKeys(boClass);
540: Vector childPrimaryKeys = collectionDescriptor
541: .getForeignKeyFields();
542:
543: if (parentForeignKeys.size() != childPrimaryKeys.size()) {
544: throw new RuntimeException(
545: "The number of keys in the class descriptor and the inverse foreign key mapping for the collection descriptors do not match.");
546: }
547: Map<String, String> fkToPkMap = new HashMap<String, String>();
548: Iterator pFKIter = parentForeignKeys.iterator();
549: Iterator cPKIterator = childPrimaryKeys.iterator();
550:
551: while (pFKIter.hasNext()) {
552: String parentForeignKey = (String) pFKIter.next();
553: String childPrimaryKey = (String) cPKIterator.next();
554:
555: fkToPkMap.put(parentForeignKey, childPrimaryKey);
556: }
557: return fkToPkMap;
558: }
559:
560: /**
561: * @see org.kuali.core.service.PersistenceService#getNestedForeignKeyMap(java.lang.Class)
562: */
563: @Cached
564: public Map getNestedForeignKeyMap(Class persistableObjectClass) {
565: Map fkMap = new HashMap();
566:
567: ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
568: Vector objectReferences = classDescriptor
569: .getObjectReferenceDescriptors();
570: for (Iterator iter = objectReferences.iterator(); iter
571: .hasNext();) {
572: ObjectReferenceDescriptor objectReferenceDescriptor = (ObjectReferenceDescriptor) iter
573: .next();
574: ClassDescriptor referenceDescriptor = this
575: .getClassDescriptor(objectReferenceDescriptor
576: .getItemClass());
577:
578: FieldDescriptor[] fkFields = objectReferenceDescriptor
579: .getForeignKeyFieldDescriptors(classDescriptor);
580: FieldDescriptor[] pkFields = referenceDescriptor
581: .getPkFields();
582: for (int i = 0; i < pkFields.length; i++) {
583: FieldDescriptor pkField = pkFields[i];
584: fkMap.put(objectReferenceDescriptor.getAttributeName()
585: + "." + pkField.getAttributeName(), fkFields[i]
586: .getAttributeName());
587: }
588: }
589:
590: return fkMap;
591: }
592:
593: /**
594: * @see org.kuali.core.service.PersistenceMetadataService#hasPrimaryKeyFieldValues(java.lang.Object)
595: */
596: public boolean hasPrimaryKeyFieldValues(Object persistableObject) {
597: Map keyFields = getPrimaryKeyFieldValues(persistableObject);
598:
599: boolean emptyField = false;
600: for (Iterator i = keyFields.entrySet().iterator(); !emptyField
601: && i.hasNext();) {
602: Map.Entry e = (Map.Entry) i.next();
603:
604: Object fieldValue = e.getValue();
605: if (fieldValue == null) {
606: emptyField = true;
607: } else if (fieldValue instanceof String) {
608: if (StringUtils.isEmpty((String) fieldValue)) {
609: emptyField = true;
610: } else {
611: emptyField = false;
612: }
613: }
614: }
615:
616: return !emptyField;
617: }
618:
619: /**
620: * @see org.kuali.core.service.PersistenceService#getForeignKeyFieldsPopulationState(org.kuali.core.bo.BusinessObject,
621: * java.lang.String)
622: */
623: public ForeignKeyFieldsPopulationState getForeignKeyFieldsPopulationState(
624: PersistableBusinessObject bo, String referenceName) {
625:
626: boolean allFieldsPopulated = true;
627: boolean anyFieldsPopulated = false;
628: List<String> unpopulatedFields = new ArrayList<String>();
629:
630: // yelp if nulls were passed in
631: if (bo == null) {
632: throw new IllegalArgumentException(
633: "The Class passed in for the BusinessObject argument was null.");
634: }
635: if (StringUtils.isBlank(referenceName)) {
636: throw new IllegalArgumentException(
637: "The String passed in for the referenceName argument was null or empty.");
638: }
639:
640: PropertyDescriptor propertyDescriptor = null;
641:
642: // make sure the attribute exists at all, throw exception if not
643: try {
644: propertyDescriptor = PropertyUtils.getPropertyDescriptor(
645: bo, referenceName);
646: } catch (Exception e) {
647: throw new RuntimeException(e);
648: }
649: if (propertyDescriptor == null) {
650: throw new ReferenceAttributeDoesntExistException(
651: "Requested attribute: '" + referenceName
652: + "' does not exist " + "on class: '"
653: + bo.getClass().getName() + "'.");
654: }
655:
656: // get the class of the attribute name
657: Class referenceClass = propertyDescriptor.getPropertyType();
658:
659: // make sure the class of the attribute descends from BusinessObject,
660: // otherwise throw an exception
661: if (!PersistableBusinessObject.class
662: .isAssignableFrom(referenceClass)) {
663: throw new ObjectNotABusinessObjectRuntimeException(
664: "Attribute requested ("
665: + referenceName
666: + ") is of class: "
667: + "'"
668: + referenceClass.getName()
669: + "' and is not a "
670: + "descendent of BusinessObject. Only descendents of BusinessObject "
671: + "can be used.");
672: }
673:
674: // make sure the attribute designated is listed as a reference-descriptor
675: // on the clazz specified, otherwise throw an exception (OJB); UniversalUser objects
676: // will be excluded from this
677: ClassDescriptor classDescriptor = getClassDescriptor(bo
678: .getClass());
679: if (!UniversalUser.class.equals(referenceClass)) {
680: ObjectReferenceDescriptor referenceDescriptor = classDescriptor
681: .getObjectReferenceDescriptorByName(referenceName);
682: if (referenceDescriptor == null) {
683: throw new ReferenceAttributeNotAnOjbReferenceException(
684: "Attribute requested ("
685: + referenceName
686: + ") is not listed "
687: + "in OJB as a reference-descriptor for class: '"
688: + bo.getClass().getName() + "'");
689: }
690:
691: // get the list of the foreign-keys for this reference-descriptor (OJB)
692: Vector fkFields = referenceDescriptor.getForeignKeyFields();
693: Iterator fkIterator = fkFields.iterator();
694:
695: // walk through the list of the foreign keys, get their types
696: while (fkIterator.hasNext()) {
697:
698: // get the field name of the fk & pk field
699: String fkFieldName = (String) fkIterator.next();
700:
701: // get the value for the fk field
702: Object fkFieldValue = null;
703: try {
704: fkFieldValue = PropertyUtils.getSimpleProperty(bo,
705: fkFieldName);
706: }
707:
708: // abort if the value is not retrievable
709: catch (Exception e) {
710: throw new RuntimeException(e);
711: }
712:
713: // test the value
714: if (fkFieldValue == null) {
715: allFieldsPopulated = false;
716: unpopulatedFields.add(fkFieldName);
717: } else if (fkFieldValue instanceof String) {
718: if (StringUtils.isBlank((String) fkFieldValue)) {
719: allFieldsPopulated = false;
720: unpopulatedFields.add(fkFieldName);
721: } else {
722: anyFieldsPopulated = true;
723: }
724: } else {
725: anyFieldsPopulated = true;
726: }
727: }
728:
729: }
730:
731: // sanity check. if the flag for all fields populated is set, then
732: // there should be nothing in the unpopulatedFields list
733: if (allFieldsPopulated) {
734: if (!unpopulatedFields.isEmpty()) {
735: throw new RuntimeException(
736: "The flag is set that indicates all fields are populated, but there "
737: + "are fields present in the unpopulatedFields list. This should never happen, and indicates "
738: + "that the logic in this method is broken.");
739: }
740: }
741:
742: return new ForeignKeyFieldsPopulationState(allFieldsPopulated,
743: anyFieldsPopulated, unpopulatedFields);
744:
745: }
746:
747: /**
748: * @see org.kuali.core.service.PersistenceStructureService#listReferenceObjectFieldNames(java.lang.Class)
749: */
750: @Cached
751: public Map<String, Class> listReferenceObjectFields(Class boClass) {
752:
753: // validate parameter
754: if (boClass == null) {
755: throw new IllegalArgumentException(
756: "Class specified in the parameter was null.");
757: }
758: if (!PersistableBusinessObject.class.isAssignableFrom(boClass)) {
759: throw new IllegalArgumentException("Class specified ["
760: + boClass.getName() + "] must be a class that "
761: + "inherits from BusinessObject.");
762: }
763:
764: ClassDescriptor classDescriptor = getClassDescriptor(boClass);
765: Collection<ObjectReferenceDescriptor> referenceDescriptors = classDescriptor
766: .getObjectReferenceDescriptors(true);
767:
768: Map<String, Class> references = new HashMap();
769: for (ObjectReferenceDescriptor referenceDescriptor : referenceDescriptors) {
770: references.put(referenceDescriptor.getAttributeName(),
771: referenceDescriptor.getItemClass());
772: }
773: return references;
774: }
775:
776: @Cached
777: public Map<String, Class> listCollectionObjectTypes(Class boClass) {
778:
779: if (boClass == null) {
780: throw new IllegalArgumentException(
781: "Class specified in the parameter was null.");
782: }
783:
784: Map<String, Class> references = new HashMap();
785: ClassDescriptor classDescriptor = null;
786:
787: try {
788: classDescriptor = getClassDescriptor(boClass);
789: } catch (ClassNotPersistableException cnpe) {
790: return references;
791: }
792:
793: Collection<CollectionDescriptor> collectionDescriptors = classDescriptor
794: .getCollectionDescriptors(true);
795:
796: for (CollectionDescriptor collectionDescriptor : collectionDescriptors) {
797: references.put(collectionDescriptor.getAttributeName(),
798: collectionDescriptor.getItemClass());
799: }
800: return references;
801: }
802:
803: public Map<String, Class> listCollectionObjectTypes(
804: PersistableBusinessObject bo) {
805:
806: // validate parameter
807: if (bo == null) {
808: throw new IllegalArgumentException(
809: "BO specified in the parameter was null.");
810: }
811: if (!(bo instanceof PersistableBusinessObject)) {
812: throw new IllegalArgumentException("BO specified ["
813: + bo.getClass().getName()
814: + "] must be a class that "
815: + "inherits from BusinessObject.");
816: }
817:
818: return listCollectionObjectTypes(bo.getClass());
819: }
820:
821: /**
822: * @see org.kuali.core.service.PersistenceStructureService#listReferenceObjectFieldNames(org.kuali.core.bo.BusinessObject)
823: */
824: public Map<String, Class> listReferenceObjectFields(
825: PersistableBusinessObject bo) {
826:
827: // validate parameter
828: if (bo == null) {
829: throw new IllegalArgumentException(
830: "BO specified in the parameter was null.");
831: }
832: if (!(bo instanceof PersistableBusinessObject)) {
833: throw new IllegalArgumentException("BO specified ["
834: + bo.getClass().getName()
835: + "] must be a class that "
836: + "inherits from BusinessObject.");
837: }
838:
839: return listReferenceObjectFields(bo.getClass());
840: }
841:
842: @Cached
843: public boolean isReferenceUpdatable(Class boClass,
844: String referenceName) {
845: ClassDescriptor classDescriptor = getClassDescriptor(boClass);
846: ObjectReferenceDescriptor refDesc = classDescriptor
847: .getObjectReferenceDescriptorByName(referenceName);
848: return refDesc.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE;
849: }
850:
851: @Cached
852: public boolean isCollectionUpdatable(Class boClass,
853: String collectionName) {
854: ClassDescriptor cd = getClassDescriptor(boClass);
855: CollectionDescriptor collDesc = cd
856: .getCollectionDescriptorByName(collectionName);
857: return collDesc.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE;
858: }
859:
860: @Cached
861: public boolean hasCollection(Class boClass, String collectionName) {
862: ClassDescriptor cd = getClassDescriptor(boClass);
863: return cd.getCollectionDescriptorByName(collectionName) != null;
864: }
865:
866: @Cached
867: public boolean hasReference(Class boClass, String referenceName) {
868: ClassDescriptor cd = getClassDescriptor(boClass);
869: return cd.getObjectReferenceDescriptorByName(referenceName) != null;
870: }
871: }
|