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