001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.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/ecl2.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 */
016package org.kuali.rice.krad.service.impl;
017
018import java.beans.PropertyDescriptor;
019import java.lang.reflect.InvocationTargetException;
020import java.util.ArrayList;
021import java.util.Collection;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026import java.util.Vector;
027
028import org.apache.commons.beanutils.PropertyUtils;
029import org.apache.commons.lang.StringUtils;
030import org.apache.ojb.broker.metadata.ClassDescriptor;
031import org.apache.ojb.broker.metadata.CollectionDescriptor;
032import org.apache.ojb.broker.metadata.FieldDescriptor;
033import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
034import org.apache.ojb.broker.metadata.SuperReferenceDescriptor;
035import org.kuali.rice.krad.bo.DataObjectRelationship;
036import org.kuali.rice.krad.bo.PersistableBusinessObject;
037import org.kuali.rice.krad.exception.ClassNotPersistableException;
038import org.kuali.rice.krad.exception.IntrospectionException;
039import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException;
040import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException;
041import org.kuali.rice.krad.exception.ReferenceAttributeNotAnOjbReferenceException;
042import org.kuali.rice.krad.service.PersistenceStructureService;
043import org.kuali.rice.krad.util.ForeignKeyFieldsPopulationState;
044
045public class PersistenceStructureServiceOjbImpl extends PersistenceServiceImplBase implements PersistenceStructureService {
046
047        /**
048         * 
049         * special case when the attributeClass passed in doesnt match the class of
050         * the reference-descriptor as defined in ojb-repository. Currently the only
051         * case of this happening is ObjectCode vs. ObjectCodeCurrent.
052         * 
053         * NOTE: This method makes no real sense and is a product of a hack
054         * introduced by KFS for an unknown reason. If you find yourself using this
055         * map stop and go do something else.
056         * 
057         * @param from
058         *            the class in the code
059         * @param to
060         *            the class in the repository
061         */
062        public static Map<Class, Class> referenceConversionMap = new HashMap<Class, Class>();
063
064        
065        private PersistenceStructureService persistenceStructureServiceJpa;
066
067        public PersistenceStructureService getPersistenceStructureServiceJpa() {
068                return this.persistenceStructureServiceJpa;
069        }
070
071        public void setPersistenceStructureServiceJpa(PersistenceStructureService persistenceStructureServiceJpa) {
072                this.persistenceStructureServiceJpa = persistenceStructureServiceJpa;
073        }
074        
075        /**
076         * @see org.kuali.rice.krad.service.PersistenceService#isPersistable(java.lang.Class)
077         */
078        
079        public boolean isPersistable(Class clazz) {
080                boolean isPersistable = false;
081                try {
082                        if (getClassDescriptor(clazz) != null) {
083                                isPersistable = true;
084                        }
085                } catch (ClassNotPersistableException e) {
086                        isPersistable = false;
087                }
088                return isPersistable;
089        }
090
091        /**
092         * @see org.kuali.rice.krad.service.PersistenceService#getPrimaryKeys(java.lang.Class)
093         */
094        
095        public List getPrimaryKeys(Class clazz) {
096                List pkList = new ArrayList();
097                ClassDescriptor classDescriptor = getClassDescriptor(clazz);
098                FieldDescriptor keyDescriptors[] = classDescriptor.getPkFields();
099                for (int i = 0; i < keyDescriptors.length; ++i) {
100                        FieldDescriptor keyDescriptor = keyDescriptors[i];
101                        pkList.add(keyDescriptor.getAttributeName());
102                }
103                return pkList;
104        }
105
106        /**
107         * @see org.kuali.rice.krad.service.PersistenceMetadataExplorerService#listFieldNames(java.lang.Class)
108         */
109        
110        public List listFieldNames(Class clazz) {
111                List fieldNames = new ArrayList(); 
112                ClassDescriptor classDescriptor = getClassDescriptor(clazz);
113                FieldDescriptor fieldDescriptors[] = classDescriptor.getFieldDescriptions();
114                for (int i = 0; i < fieldDescriptors.length; ++i) {
115                        FieldDescriptor fieldDescriptor = fieldDescriptors[i];
116                        fieldNames.add(fieldDescriptor.getAttributeName());
117                }
118                return fieldNames;
119        }
120
121        /**
122         * @see org.kuali.rice.krad.service.PersistenceMetadataService#clearPrimaryKeyFields(java.lang.Object)
123         */
124        // Unit tests only
125        public Object clearPrimaryKeyFields(Object persistableObject) {
126                if (persistableObject == null) {
127                        throw new IllegalArgumentException("invalid (null) persistableObject");
128                }
129
130                String className = null;
131                String fieldName = null;
132                try {
133                        className = persistableObject.getClass().getName();
134                        List fields = listPrimaryKeyFieldNames(persistableObject.getClass());
135                        for (Iterator i = fields.iterator(); i.hasNext();) {
136                                fieldName = (String) i.next();
137
138                                PropertyUtils.setProperty(persistableObject, fieldName, null);
139                        }
140
141                        if (persistableObject instanceof PersistableBusinessObject) {
142                                ((PersistableBusinessObject) persistableObject).setObjectId(null);
143                        }
144                } catch (NoSuchMethodException e) {
145                        throw new IntrospectionException("no setter for property '" + className + "." + fieldName + "'", e);
146                } catch (IllegalAccessException e) {
147                        throw new IntrospectionException("problem accessing property '" + className + "." + fieldName + "'", e);
148                } catch (InvocationTargetException e) {
149                        throw new IntrospectionException("problem invoking getter for property '" + className + "." + fieldName + "'", e);
150                }
151
152                return persistableObject;
153        }
154
155        /**
156         * @see org.kuali.rice.krad.service.PersistenceMetadataExplorerService#listPersistableSubclasses(java.lang.Class)
157         */
158        
159        // Unit tests only
160        public List listPersistableSubclasses(Class superclazz) {
161                if (superclazz == null) {
162                        throw new IllegalArgumentException("invalid (null) uberclass");
163                }
164
165                Map allDescriptors = getDescriptorRepository().getDescriptorTable();
166                List persistableSubclasses = new ArrayList();
167                for (Iterator i = allDescriptors.entrySet().iterator(); i.hasNext();) {
168                        Map.Entry e = (Map.Entry) i.next();
169
170                        Class persistableClass = ((ClassDescriptor) e.getValue()).getClassOfObject();
171                        if (!superclazz.equals(persistableClass) && superclazz.isAssignableFrom(persistableClass)) {
172                                persistableSubclasses.add(persistableClass);
173                        }
174                }
175                return persistableSubclasses;
176        }
177
178        /**
179         * @see org.kuali.rice.krad.service.PersistenceService#getRelationshipMetadata(java.lang.Class,
180         *      java.lang.String)
181         */
182        
183        public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName, String attributePrefix) {
184                if (persistableClass == null) {
185                        throw new IllegalArgumentException("invalid (null) persistableClass");
186                }
187                if (StringUtils.isBlank(attributeName)) {
188                        throw new IllegalArgumentException("invalid (blank) attributeName");
189                }
190
191                Map<String, DataObjectRelationship> relationships = new HashMap<String, DataObjectRelationship>();
192                ClassDescriptor classDescriptor = getClassDescriptor(persistableClass);
193                Vector<ObjectReferenceDescriptor> references = classDescriptor.getObjectReferenceDescriptors();
194                for (ObjectReferenceDescriptor objRef : references) {
195                        Vector fks = objRef.getForeignKeyFields();
196                        if (fks.contains(attributeName) || objRef.getAttributeName().equals(attributeName)) {
197                                Map<String, String> fkToPkRefs = getForeignKeysForReference(persistableClass, objRef.getAttributeName());
198                                DataObjectRelationship rel = new DataObjectRelationship(persistableClass, objRef.getAttributeName(), objRef.getItemClass());
199                                for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) {
200                                        if (StringUtils.isBlank(attributePrefix)) {
201                                                rel.getParentToChildReferences().put(ref.getKey(), ref.getValue());
202                                        } else {
203                                                rel.getParentToChildReferences().put(attributePrefix + "." + ref.getKey(), ref.getValue());
204                                        }
205                                }
206                                relationships.put(objRef.getAttributeName(), rel);
207                        }
208                }
209                return relationships;
210        }
211
212        
213        // Unit tests only
214        public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName) {
215                return getRelationshipMetadata(persistableClass, attributeName, null);
216        }
217
218        /**
219         * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeyFieldName(java.lang.Object,
220         *      java.lang.String, java.lang.String)
221         */
222        
223        public String getForeignKeyFieldName(Class persistableObjectClass, String attributeName, String pkName) {
224                String fkName = "";
225                ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
226                ObjectReferenceDescriptor objectReferenceDescriptor = classDescriptor.getObjectReferenceDescriptorByName(attributeName);
227                if (objectReferenceDescriptor == null) {
228                        throw new RuntimeException("Attribute name " + attributeName + " is not a valid reference to class " + persistableObjectClass.getName());
229                }
230                ClassDescriptor referenceDescriptor = this.getClassDescriptor(objectReferenceDescriptor.getItemClass());
231
232                FieldDescriptor[] fkFields = objectReferenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
233                FieldDescriptor[] pkFields = referenceDescriptor.getPkFields();
234                for (int i = 0; i < pkFields.length; i++) {
235                        FieldDescriptor pkField = pkFields[i];
236                        if (pkField.getAttributeName().equals(pkName)) {
237                                fkName = fkFields[i].getAttributeName();
238                        }
239                }
240                return fkName;
241        }
242
243        /**
244         * @see org.kuali.rice.krad.service.PersistenceService#getReferencesForForeignKey(java.lang.Class,
245         *      java.lang.String)
246         */
247        
248        public Map getReferencesForForeignKey(Class persistableObjectClass, String attributeName) {
249                Map referenceClasses = new HashMap();
250                if (PersistableBusinessObject.class.isAssignableFrom(persistableObjectClass)) {
251                        ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
252                        Vector objectReferences = classDescriptor.getObjectReferenceDescriptors();
253                        for (Iterator iter = objectReferences.iterator(); iter.hasNext();) {
254                                ObjectReferenceDescriptor referenceDescriptor = (ObjectReferenceDescriptor) iter.next();
255
256                                /*
257                                 * iterate through the fk keys for the reference object and if
258                                 * matches the attributeName add the class as a reference
259                                 */
260                                FieldDescriptor[] refFkNames = referenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
261                                for (int i = 0; i < refFkNames.length; i++) {
262                                        FieldDescriptor fkField = refFkNames[i];
263                                        if (fkField.getAttributeName().equals(attributeName)) {
264                                                referenceClasses.put(referenceDescriptor.getAttributeName(), referenceDescriptor.getItemClass());
265                                        }
266                                }
267                        }
268                }
269                return referenceClasses;
270        }
271
272        /**
273         * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeysForReference(java.lang.Class,
274         *      java.lang.String) The Map structure is: Key(String fkFieldName) =>
275         *      Value(String pkFieldName) NOTE that this implementation depends on
276         *      the ordering of foreign-key elements in the ojb-repository matching
277         *      the ordering of primary-key declarations of the class on the other
278         *      side of the relationship. This is done because: 1. The current
279         *      version of OJB requires you to declare all of these things in the
280         *      correct (and matching) order in the ojb-repository file for it to
281         *      work at all. 2. There is no other way to match a given foreign-key
282         *      reference to its corresponding primary-key on the opposing side of
283         *      the relationship. Yes, this is a crummy way to do it, but OJB doesnt
284         *      provide explicit matches of foreign-keys to primary keys, and always
285         *      assumes that foreign-keys map to primary keys on the other object,
286         *      and never to a set of candidate keys, or any other column.
287         */
288        
289        public Map getForeignKeysForReference(Class clazz, String attributeName) {
290                // yelp if nulls were passed in
291                if (clazz == null) {
292                        throw new IllegalArgumentException("The Class passed in for the clazz argument was null.");
293                }
294                if (attributeName == null) {
295                        throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
296                }
297
298                // get the class of the attribute name
299                Class attributeClass = getBusinessObjectAttributeClass(clazz, attributeName);
300                if (attributeClass == null) {
301                        throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + attributeName + "' does not exist " + "on class: '" + clazz.getName() + "'.");
302                }
303
304                // make sure the class of the attribute descends from BusinessObject,
305                // otherwise throw an exception
306                if (!PersistableBusinessObject.class.isAssignableFrom(attributeClass)) {
307                        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.");
308                }
309
310                Map fkMap = new HashMap();
311                
312                // make sure the attribute designated is listed as a
313                // reference-descriptor on the clazz specified, otherwise 
314                // throw an exception (OJB); 
315                ClassDescriptor classDescriptor = getClassDescriptor(clazz);
316                ObjectReferenceDescriptor referenceDescriptor = classDescriptor.getObjectReferenceDescriptorByName(attributeName);
317                if (referenceDescriptor == null) {
318                        throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + attributeName + ") is not listed " + "in OJB as a reference-descriptor for class: '" + clazz.getName() + "'");
319                }
320
321                // special case when the attributeClass passed in doesnt match the
322                // class of the reference-descriptor as defined in ojb-repository.
323                // Currently
324                // the only case of this happening is ObjectCode vs.
325                // ObjectCodeCurrent.
326                if (!attributeClass.equals(referenceDescriptor.getItemClass())) {
327
328                        if (referenceConversionMap.containsKey(attributeClass)) {
329                                attributeClass = referenceConversionMap.get(attributeClass);
330                        } else {
331                                throw new RuntimeException("The Class of the Java member [" + attributeClass.getName() + "] '" + attributeName + "' does not match the class of the " + "reference-descriptor [" + referenceDescriptor.getItemClass().getName() + "]. " + "This is an unhandled special case for which special code needs to be written " + "in this class.");
332                        }
333                }
334
335                // get the list of the foreign-keys for this reference-descriptor
336                // (OJB)
337                Vector fkFields = referenceDescriptor.getForeignKeyFields();
338                Iterator fkIterator = fkFields.iterator();
339
340                // get the list of the corresponding pk fields on the other side of
341                // the relationship
342                List pkFields = getPrimaryKeys(attributeClass);
343                Iterator pkIterator = pkFields.iterator();
344
345                // make sure the size of the pkIterator is the same as the
346                // size of the fkIterator, otherwise this whole thing is borked
347                if (pkFields.size() != fkFields.size()) {
348                        throw new RuntimeException("KualiPersistenceStructureService Error: The number of " + "foreign keys doesnt match the number of primary keys.  This may be a " + "result of misconfigured OJB-repository files.");
349                }
350
351                // walk through the list of the foreign keys, get their types
352                while (fkIterator.hasNext()) {
353                        // if there is a next FK but not a next PK, then we've got a big
354                        // problem,
355                        // and cannot continue
356                        if (!pkIterator.hasNext()) {
357                                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, as it means that the OJB repository " + "files are misconfigured.");
358                        }
359
360                        // get the field name of the fk & pk field
361                        String fkFieldName = (String) fkIterator.next();
362                        String pkFieldName = (String) pkIterator.next();
363
364                        // add the fieldName and fieldType to the map
365                        fkMap.put(fkFieldName, pkFieldName);
366                }
367                
368                return fkMap;
369        }
370
371        
372        public Map<String, String> getInverseForeignKeysForCollection(Class boClass, String collectionName) {
373                // yelp if nulls were passed in
374                if (boClass == null) {
375                        throw new IllegalArgumentException("The Class passed in for the boClass argument was null.");
376                }
377                if (collectionName == null) {
378                        throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
379                }
380
381                PropertyDescriptor propertyDescriptor = null;
382
383                // make an instance of the class passed
384                Object classInstance;
385                try {
386                        classInstance = boClass.newInstance();
387                } catch (Exception e) {
388                        throw new RuntimeException(e);
389                }
390
391                // make sure the attribute exists at all, throw exception if not
392                try {
393                        propertyDescriptor = PropertyUtils.getPropertyDescriptor(classInstance, collectionName);
394                } catch (Exception e) {
395                        throw new RuntimeException(e);
396                }
397                if (propertyDescriptor == null) {
398                        throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + collectionName + "' does not exist " + "on class: '" + boClass.getName() + "'. GFK");
399                }
400
401                // get the class of the attribute name
402                Class attributeClass = propertyDescriptor.getPropertyType();
403
404                // make sure the class of the attribute descends from BusinessObject,
405                // otherwise throw an exception
406                if (!Collection.class.isAssignableFrom(attributeClass)) {
407                        throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + collectionName + ") is of class: " + "'" + attributeClass.getName() + "' and is not a " + "descendent of Collection");
408                }
409
410                // make sure the collection designated is listed as a
411                // collection-descriptor
412                // on the boClass specified, otherwise throw an exception
413                ClassDescriptor classDescriptor = getClassDescriptor(boClass);
414                CollectionDescriptor collectionDescriptor = classDescriptor.getCollectionDescriptorByName(collectionName);
415
416                // in collections, the number of keys is equal to the number of keys in
417                // the parent class (the class with the collection).
418                // Each of the primary keys on the parent object will be mapped to a
419                // field in the element object.
420
421                List parentForeignKeys = getPrimaryKeys(boClass);
422                Vector childPrimaryKeysLegacy = collectionDescriptor.getForeignKeyFields();
423
424                if (parentForeignKeys.size() != childPrimaryKeysLegacy.size()) {
425                        throw new RuntimeException("The number of keys in the class descriptor and the inverse foreign key mapping for the collection descriptors do not match.");
426                }
427
428                Map<String, String> fkToPkMap = new HashMap<String, String>();
429
430                Iterator pFKIter = parentForeignKeys.iterator();
431                Iterator cPKIterator = childPrimaryKeysLegacy.iterator();
432
433                while (pFKIter.hasNext()) {
434                        String parentForeignKey = (String) pFKIter.next();
435                        String childPrimaryKey = (String) cPKIterator.next();
436
437                        fkToPkMap.put(parentForeignKey, childPrimaryKey);
438                }
439                                
440                return fkToPkMap;
441        }
442
443        /**
444         * @see org.kuali.rice.krad.service.PersistenceService#getNestedForeignKeyMap(java.lang.Class)
445         */
446        
447        public Map getNestedForeignKeyMap(Class persistableObjectClass) {
448                Map fkMap = new HashMap(); 
449                ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
450                Vector objectReferences = classDescriptor.getObjectReferenceDescriptors();
451                for (Iterator iter = objectReferences.iterator(); iter.hasNext();) {
452                        ObjectReferenceDescriptor objectReferenceDescriptor = (ObjectReferenceDescriptor) iter.next();
453                        ClassDescriptor referenceDescriptor = this.getClassDescriptor(objectReferenceDescriptor.getItemClass());
454
455                        FieldDescriptor[] fkFields = objectReferenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
456                        FieldDescriptor[] pkFields = referenceDescriptor.getPkFields();
457                        for (int i = 0; i < pkFields.length; i++) {
458                                FieldDescriptor pkField = pkFields[i];
459                                fkMap.put(objectReferenceDescriptor.getAttributeName() + "." + pkField.getAttributeName(), fkFields[i].getAttributeName());
460                        }
461                }
462                
463                return fkMap;
464        }
465
466        /**
467         * @see org.kuali.rice.krad.service.PersistenceMetadataService#hasPrimaryKeyFieldValues(java.lang.Object)
468         */
469        public boolean hasPrimaryKeyFieldValues(Object persistableObject) {
470                Map keyFields = getPrimaryKeyFieldValues(persistableObject);
471
472                boolean emptyField = false;
473                for (Iterator i = keyFields.entrySet().iterator(); !emptyField && i.hasNext();) {
474                        Map.Entry e = (Map.Entry) i.next();
475
476                        Object fieldValue = e.getValue();
477                        if (fieldValue == null) {
478                                emptyField = true;
479                        } else if (fieldValue instanceof String) {
480                                if (StringUtils.isEmpty((String) fieldValue)) {
481                                        emptyField = true;
482                                } else {
483                                        emptyField = false;
484                                }
485                        }
486                }
487
488                return !emptyField;
489        }
490
491        /**
492         * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeyFieldsPopulationState(org.kuali.rice.krad.bo.BusinessObject,
493         *      java.lang.String)
494         */
495        public ForeignKeyFieldsPopulationState getForeignKeyFieldsPopulationState(PersistableBusinessObject bo, String referenceName) {
496                boolean allFieldsPopulated = true;
497                boolean anyFieldsPopulated = false;
498                List<String> unpopulatedFields = new ArrayList<String>();
499
500                // yelp if nulls were passed in
501                if (bo == null) {
502                        throw new IllegalArgumentException("The Class passed in for the BusinessObject argument was null.");
503                }
504                if (StringUtils.isBlank(referenceName)) {
505                        throw new IllegalArgumentException("The String passed in for the referenceName argument was null or empty.");
506                }
507
508                PropertyDescriptor propertyDescriptor = null;
509
510                // make sure the attribute exists at all, throw exception if not
511                try {
512                        propertyDescriptor = PropertyUtils.getPropertyDescriptor(bo, referenceName);
513                } catch (Exception e) {
514                        throw new RuntimeException(e);
515                }
516                if (propertyDescriptor == null) {
517                        throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + referenceName + "' does not exist " + "on class: '" + bo.getClass().getName() + "'.");
518                }
519
520                // get the class of the attribute name
521                Class referenceClass = propertyDescriptor.getPropertyType();
522
523                // make sure the class of the attribute descends from BusinessObject,
524                // otherwise throw an exception
525                if (!PersistableBusinessObject.class.isAssignableFrom(referenceClass)) {
526                        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.");
527                }
528
529                // make sure the attribute designated is listed as a
530                // reference-descriptor
531                // on the clazz specified, otherwise throw an exception (OJB);
532
533                ClassDescriptor classDescriptor = getClassDescriptor(bo.getClass());
534
535                // This block is a combination of legacy and jpa
536                ObjectReferenceDescriptor referenceDescriptor = classDescriptor.getObjectReferenceDescriptorByName(referenceName);
537                if (referenceDescriptor == null) {
538                        throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + referenceName + ") is not listed " + "in OJB as a reference-descriptor for class: '" + bo.getClass().getName() + "'");
539                }
540
541                // get the list of the foreign-keys for this reference-descriptor
542                Vector fkFieldsLegacy = referenceDescriptor.getForeignKeyFields();
543                Iterator fkIteratorLegacy = fkFieldsLegacy.iterator();
544
545                // walk through the list of the foreign keys, get their types
546                while (fkIteratorLegacy.hasNext()) {
547
548                        // get the field name of the fk & pk field
549                        String fkFieldName = (String) fkIteratorLegacy.next();
550
551                        // get the value for the fk field
552                        Object fkFieldValue = null;
553                        try {
554                                fkFieldValue = PropertyUtils.getSimpleProperty(bo, fkFieldName);
555                        }
556
557                        // abort if the value is not retrievable
558                        catch (Exception e) {
559                                throw new RuntimeException(e);
560                        }
561
562                        // test the value
563                        if (fkFieldValue == null) {
564                                allFieldsPopulated = false;
565                                unpopulatedFields.add(fkFieldName);
566                        } else if (fkFieldValue instanceof String) {
567                                if (StringUtils.isBlank((String) fkFieldValue)) {
568                                        allFieldsPopulated = false;
569                                        unpopulatedFields.add(fkFieldName);
570                                } else {
571                                        anyFieldsPopulated = true;
572                                }
573                        } else {
574                                anyFieldsPopulated = true;
575                        }
576                }
577
578                // sanity check. if the flag for all fields populated is set, then
579                // there should be nothing in the unpopulatedFields list
580                if (allFieldsPopulated) {
581                        if (!unpopulatedFields.isEmpty()) {
582                                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.");
583                        }
584                }
585                
586                return new ForeignKeyFieldsPopulationState(allFieldsPopulated, anyFieldsPopulated, unpopulatedFields);
587        }
588
589        /**
590         * @see org.kuali.rice.krad.service.PersistenceStructureService#listReferenceObjectFieldNames(java.lang.Class)
591         */
592        
593        public Map<String, Class> listReferenceObjectFields(Class boClass) {
594                // validate parameter
595                if (boClass == null) {
596                        throw new IllegalArgumentException("Class specified in the parameter was null.");
597                }
598                if (!PersistableBusinessObject.class.isAssignableFrom(boClass)) {
599                        throw new IllegalArgumentException("Class specified [" + boClass.getName() + "] must be a class that " + "inherits from BusinessObject.");
600                }
601
602                Map<String, Class> references = new HashMap<String, Class>();
603                ClassDescriptor classDescriptor = getClassDescriptor(boClass);
604                Collection<ObjectReferenceDescriptor> referenceDescriptors = classDescriptor.getObjectReferenceDescriptors(true);
605
606                for (ObjectReferenceDescriptor referenceDescriptor : referenceDescriptors) {
607                        /*
608             * Below check is performed for OJB specific inheritance implementation. For more information see the OJB
609             * documentation: http://db.apache.org/ojb/docu/guides/advanced-technique.html#table-per-subclass
610             */
611            String superReferenceDescriptor = referenceDescriptor.getAttributeName();
612            if (!SuperReferenceDescriptor.SUPER_FIELD_INTERNAL_NAME.equals(superReferenceDescriptor)) {
613                references.put(superReferenceDescriptor, referenceDescriptor.getItemClass());
614            }
615                }
616                
617                return references;
618        }
619
620        
621        public Map<String, Class> listCollectionObjectTypes(Class boClass) {
622                if (boClass == null) {
623                        throw new IllegalArgumentException("Class specified in the parameter was null.");
624                }
625
626                Map<String, Class> references = new HashMap<String, Class>();
627                ClassDescriptor classDescriptor = null;
628                try {
629                        classDescriptor = getClassDescriptor(boClass);
630                } catch (ClassNotPersistableException cnpe) {
631                        return references;
632                }
633
634                Collection<CollectionDescriptor> collectionDescriptors = classDescriptor.getCollectionDescriptors(true);
635                for (CollectionDescriptor collectionDescriptor : collectionDescriptors) {
636                        references.put(collectionDescriptor.getAttributeName(), collectionDescriptor.getItemClass());
637                }
638                
639                return references;
640        }
641
642        public Map<String, Class> listCollectionObjectTypes(PersistableBusinessObject bo) {
643                // validate parameter
644                if (bo == null) {
645                        throw new IllegalArgumentException("BO specified in the parameter was null.");
646                }
647                if (!(bo instanceof PersistableBusinessObject)) {
648                        throw new IllegalArgumentException("BO specified [" + bo.getClass().getName() + "] must be a class that " + "inherits from BusinessObject.");
649                }
650
651                return listCollectionObjectTypes(bo.getClass());
652        }
653
654        /**
655         * @see org.kuali.rice.krad.service.PersistenceStructureService#listReferenceObjectFieldNames(org.kuali.rice.krad.bo.BusinessObject)
656         */
657        public Map<String, Class> listReferenceObjectFields(PersistableBusinessObject bo) {
658                // validate parameter
659                if (bo == null) {
660                        throw new IllegalArgumentException("BO specified in the parameter was null.");
661                }
662                if (!(bo instanceof PersistableBusinessObject)) {
663                        throw new IllegalArgumentException("BO specified [" + bo.getClass().getName() + "] must be a class that " + "inherits from BusinessObject.");
664                }
665
666                return listReferenceObjectFields(bo.getClass());
667        }
668
669        
670        public boolean isReferenceUpdatable(Class boClass, String referenceName) {
671                ClassDescriptor classDescriptor = getClassDescriptor(boClass);
672                ObjectReferenceDescriptor refDesc = classDescriptor.getObjectReferenceDescriptorByName(referenceName);
673                return refDesc.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE;
674        }
675
676        
677        public boolean isCollectionUpdatable(Class boClass, String collectionName) {
678                ClassDescriptor cd = getClassDescriptor(boClass);
679                CollectionDescriptor collDesc = cd.getCollectionDescriptorByName(collectionName);
680                return collDesc.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE;
681        }
682
683        
684        public boolean hasCollection(Class boClass, String collectionName) {
685                ClassDescriptor cd = getClassDescriptor(boClass);
686                return cd.getCollectionDescriptorByName(collectionName) != null;
687        }
688
689        
690        public boolean hasReference(Class boClass, String referenceName) {
691                ClassDescriptor cd = getClassDescriptor(boClass);
692                return cd.getObjectReferenceDescriptorByName(referenceName) != null;
693        }
694
695        /**
696         * This overridden method ...
697         * 
698         * @see org.kuali.rice.krad.service.PersistenceStructureService#getTableName(java.lang.Class)
699         */
700        
701        public String getTableName(Class<? extends PersistableBusinessObject> boClass) {
702                ClassDescriptor cd = getClassDescriptor(boClass);
703                return cd.getFullTableName();
704        }
705        
706        
707}
708