org.kuali.rice.krad.service.impl.PersistenceStructureServiceOjbImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.rice.krad.service.impl.PersistenceStructureServiceOjbImpl.java

Source

/**
 * Copyright 2005-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.rice.krad.service.impl;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.CollectionDescriptor;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
import org.apache.ojb.broker.metadata.SuperReferenceDescriptor;
import org.kuali.rice.krad.bo.DataObjectRelationship;
import org.kuali.rice.krad.bo.PersistableBusinessObject;
import org.kuali.rice.krad.exception.ClassNotPersistableException;
import org.kuali.rice.krad.exception.IntrospectionException;
import org.kuali.rice.krad.exception.ObjectNotABusinessObjectRuntimeException;
import org.kuali.rice.krad.exception.ReferenceAttributeDoesntExistException;
import org.kuali.rice.krad.exception.ReferenceAttributeNotAnOjbReferenceException;
import org.kuali.rice.krad.service.PersistenceStructureService;
import org.kuali.rice.krad.util.ForeignKeyFieldsPopulationState;
import org.kuali.rice.krad.util.LegacyDataFramework;

@Deprecated
@LegacyDataFramework
public class PersistenceStructureServiceOjbImpl extends PersistenceServiceImplBase
        implements PersistenceStructureService {

    /**
     *
     * special case when the attributeClass passed in doesnt match the class of
     * the reference-descriptor as defined in ojb-repository. Currently the only
     * case of this happening is ObjectCode vs. ObjectCodeCurrent.
     *
     * NOTE: This method makes no real sense and is a product of a hack
     * introduced by KFS for an unknown reason. If you find yourself using this
     * map stop and go do something else.
     *
     * @param from
     *            the class in the code
     * @param to
     *            the class in the repository
     */
    public static Map<Class, Class> referenceConversionMap = new HashMap<Class, Class>();

    private PersistenceStructureService persistenceStructureServiceJpa;

    public PersistenceStructureService getPersistenceStructureServiceJpa() {
        return this.persistenceStructureServiceJpa;
    }

    public void setPersistenceStructureServiceJpa(PersistenceStructureService persistenceStructureServiceJpa) {
        this.persistenceStructureServiceJpa = persistenceStructureServiceJpa;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceService#isPersistable(java.lang.Class)
     */

    @Override
    public boolean isPersistable(Class clazz) {
        boolean isPersistable = false;
        try {
            if (getClassDescriptor(clazz) != null) {
                isPersistable = true;
            }
        } catch (ClassNotPersistableException e) {
            isPersistable = false;
        }
        return isPersistable;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceService#getPrimaryKeys(java.lang.Class)
     */

    @Override
    public List getPrimaryKeys(Class clazz) {
        List pkList = new ArrayList();
        ClassDescriptor classDescriptor = getClassDescriptor(clazz);
        FieldDescriptor keyDescriptors[] = classDescriptor.getPkFields();
        for (int i = 0; i < keyDescriptors.length; ++i) {
            FieldDescriptor keyDescriptor = keyDescriptors[i];
            pkList.add(keyDescriptor.getAttributeName());
        }
        return pkList;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceMetadataExplorerService#listFieldNames(java.lang.Class)
     */

    @Override
    public List listFieldNames(Class clazz) {
        List fieldNames = new ArrayList();
        ClassDescriptor classDescriptor = getClassDescriptor(clazz);
        FieldDescriptor fieldDescriptors[] = classDescriptor.getFieldDescriptions();
        for (int i = 0; i < fieldDescriptors.length; ++i) {
            FieldDescriptor fieldDescriptor = fieldDescriptors[i];
            fieldNames.add(fieldDescriptor.getAttributeName());
        }
        return fieldNames;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceMetadataService#clearPrimaryKeyFields(java.lang.Object)
     */
    // Unit tests only
    @Override
    public Object clearPrimaryKeyFields(Object persistableObject) {
        if (persistableObject == null) {
            throw new IllegalArgumentException("invalid (null) persistableObject");
        }

        String className = null;
        String fieldName = null;
        try {
            className = persistableObject.getClass().getName();
            List fields = listPrimaryKeyFieldNames(persistableObject.getClass());
            for (Iterator i = fields.iterator(); i.hasNext();) {
                fieldName = (String) i.next();

                PropertyUtils.setProperty(persistableObject, fieldName, null);
            }

            if (persistableObject instanceof PersistableBusinessObject) {
                ((PersistableBusinessObject) persistableObject).setObjectId(null);
            }
        } catch (NoSuchMethodException e) {
            throw new IntrospectionException("no setter for property '" + className + "." + fieldName + "'", e);
        } catch (IllegalAccessException e) {
            throw new IntrospectionException("problem accessing property '" + className + "." + fieldName + "'", e);
        } catch (InvocationTargetException e) {
            throw new IntrospectionException(
                    "problem invoking getter for property '" + className + "." + fieldName + "'", e);
        }

        return persistableObject;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceMetadataExplorerService#listPersistableSubclasses(java.lang.Class)
     */

    // Unit tests only
    @Override
    public List listPersistableSubclasses(Class superclazz) {
        if (superclazz == null) {
            throw new IllegalArgumentException("invalid (null) uberclass");
        }

        Map allDescriptors = getDescriptorRepository().getDescriptorTable();
        List persistableSubclasses = new ArrayList();
        for (Iterator i = allDescriptors.entrySet().iterator(); i.hasNext();) {
            Map.Entry e = (Map.Entry) i.next();

            Class persistableClass = ((ClassDescriptor) e.getValue()).getClassOfObject();
            if (!superclazz.equals(persistableClass) && superclazz.isAssignableFrom(persistableClass)) {
                persistableSubclasses.add(persistableClass);
            }
        }
        return persistableSubclasses;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceService#getRelationshipMetadata(java.lang.Class,
     *      java.lang.String)
     */

    @Override
    public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass, String attributeName,
            String attributePrefix) {
        if (persistableClass == null) {
            throw new IllegalArgumentException("invalid (null) persistableClass");
        }
        if (StringUtils.isBlank(attributeName)) {
            throw new IllegalArgumentException("invalid (blank) attributeName");
        }

        Map<String, DataObjectRelationship> relationships = new HashMap<String, DataObjectRelationship>();
        ClassDescriptor classDescriptor = getClassDescriptor(persistableClass);
        Vector<ObjectReferenceDescriptor> references = classDescriptor.getObjectReferenceDescriptors();
        for (ObjectReferenceDescriptor objRef : references) {
            Vector fks = objRef.getForeignKeyFields();
            if (fks.contains(attributeName) || objRef.getAttributeName().equals(attributeName)) {
                Map<String, String> fkToPkRefs = getForeignKeysForReference(persistableClass,
                        objRef.getAttributeName());
                DataObjectRelationship rel = new DataObjectRelationship(persistableClass, objRef.getAttributeName(),
                        objRef.getItemClass());
                for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) {
                    if (StringUtils.isBlank(attributePrefix)) {
                        rel.getParentToChildReferences().put(ref.getKey(), ref.getValue());
                    } else {
                        rel.getParentToChildReferences().put(attributePrefix + "." + ref.getKey(), ref.getValue());
                    }
                }
                relationships.put(objRef.getAttributeName(), rel);
            }
        }
        return relationships;
    }

    // Unit tests only
    @Override
    public Map<String, DataObjectRelationship> getRelationshipMetadata(Class persistableClass,
            String attributeName) {
        return getRelationshipMetadata(persistableClass, attributeName, null);
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeyFieldName(java.lang.Object,
     *      java.lang.String, java.lang.String)
     */

    @Override
    public String getForeignKeyFieldName(Class persistableObjectClass, String attributeName, String pkName) {
        String fkName = "";
        ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
        ObjectReferenceDescriptor objectReferenceDescriptor = classDescriptor
                .getObjectReferenceDescriptorByName(attributeName);
        if (objectReferenceDescriptor == null) {
            throw new RuntimeException("Attribute name " + attributeName + " is not a valid reference to class "
                    + persistableObjectClass.getName());
        }
        ClassDescriptor referenceDescriptor = this.getClassDescriptor(objectReferenceDescriptor.getItemClass());

        FieldDescriptor[] fkFields = objectReferenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
        FieldDescriptor[] pkFields = referenceDescriptor.getPkFields();
        for (int i = 0; i < pkFields.length; i++) {
            FieldDescriptor pkField = pkFields[i];
            if (pkField.getAttributeName().equals(pkName)) {
                fkName = fkFields[i].getAttributeName();
            }
        }
        return fkName;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceService#getReferencesForForeignKey(java.lang.Class,
     *      java.lang.String)
     */

    @Override
    public Map getReferencesForForeignKey(Class persistableObjectClass, String attributeName) {
        Map referenceClasses = new HashMap();
        if (PersistableBusinessObject.class.isAssignableFrom(persistableObjectClass)) {
            ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
            Vector objectReferences = classDescriptor.getObjectReferenceDescriptors();
            for (Iterator iter = objectReferences.iterator(); iter.hasNext();) {
                ObjectReferenceDescriptor referenceDescriptor = (ObjectReferenceDescriptor) iter.next();

                /*
                 * iterate through the fk keys for the reference object and if
                 * matches the attributeName add the class as a reference
                 */
                FieldDescriptor[] refFkNames = referenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
                for (int i = 0; i < refFkNames.length; i++) {
                    FieldDescriptor fkField = refFkNames[i];
                    if (fkField.getAttributeName().equals(attributeName)) {
                        referenceClasses.put(referenceDescriptor.getAttributeName(),
                                referenceDescriptor.getItemClass());
                    }
                }
            }
        }
        return referenceClasses;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeysForReference(java.lang.Class,
     *      java.lang.String) The Map structure is: Key(String fkFieldName) =>
     *      Value(String pkFieldName) NOTE that this implementation depends on
     *      the ordering of foreign-key elements in the ojb-repository matching
     *      the ordering of primary-key declarations of the class on the other
     *      side of the relationship. This is done because: 1. The current
     *      version of OJB requires you to declare all of these 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 match a given foreign-key
     *      reference to its corresponding primary-key on the opposing side of
     *      the relationship. Yes, this is a crummy way to do it, but OJB doesnt
     *      provide explicit matches of foreign-keys to primary keys, and always
     *      assumes that foreign-keys map to primary keys on the other object,
     *      and never to a set of candidate keys, or any other column.
     */

    @Override
    public Map getForeignKeysForReference(Class clazz, String attributeName) {
        // yelp if nulls were passed in
        if (clazz == null) {
            throw new IllegalArgumentException("The Class passed in for the clazz argument was null.");
        }
        if (attributeName == null) {
            throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
        }

        // get the class of the attribute name
        Class attributeClass = getBusinessObjectAttributeClass(clazz, attributeName);
        if (attributeClass == null) {
            throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + attributeName
                    + "' does not exist " + "on class: '" + clazz.getName() + "'.");
        }

        // make sure the class of the attribute descends from BusinessObject,
        // otherwise throw an exception
        if (!PersistableBusinessObject.class.isAssignableFrom(attributeClass)) {
            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.");
        }

        Map fkMap = new HashMap();

        // make sure the attribute designated is listed as a
        // reference-descriptor on the clazz specified, otherwise
        // throw an exception (OJB);
        ClassDescriptor classDescriptor = getClassDescriptor(clazz);
        ObjectReferenceDescriptor referenceDescriptor = classDescriptor
                .getObjectReferenceDescriptorByName(attributeName);
        if (referenceDescriptor == null) {
            throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + attributeName
                    + ") is not listed " + "in OJB as a reference-descriptor for class: '" + clazz.getName() + "'");
        }

        // special case when the attributeClass passed in doesnt match the
        // class of the reference-descriptor as defined in ojb-repository.
        // Currently
        // the only case of this happening is ObjectCode vs.
        // ObjectCodeCurrent.
        if (!attributeClass.equals(referenceDescriptor.getItemClass())) {

            if (referenceConversionMap.containsKey(attributeClass)) {
                attributeClass = referenceConversionMap.get(attributeClass);
            } else {
                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.");
            }
        }

        // get the list of the foreign-keys for this reference-descriptor
        // (OJB)
        Vector fkFields = referenceDescriptor.getForeignKeyFields();
        Iterator fkIterator = fkFields.iterator();

        // get the list of the corresponding pk fields on the other side of
        // the relationship
        List pkFields = getPrimaryKeys(attributeClass);
        Iterator pkIterator = pkFields.iterator();

        // make sure the size of the pkIterator is the same as the
        // size of the fkIterator, otherwise this whole thing is borked
        if (pkFields.size() != fkFields.size()) {
            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.");
        }

        // walk through the list of the foreign keys, get their types
        while (fkIterator.hasNext()) {
            // if there is a next FK but not a next PK, then we've got a big
            // problem,
            // and cannot continue
            if (!pkIterator.hasNext()) {
                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.");
            }

            // get the field name of the fk & pk field
            String fkFieldName = (String) fkIterator.next();
            String pkFieldName = (String) pkIterator.next();

            // add the fieldName and fieldType to the map
            fkMap.put(fkFieldName, pkFieldName);
        }

        return fkMap;
    }

    @Override
    public Map<String, String> getInverseForeignKeysForCollection(Class boClass, String collectionName) {
        // yelp if nulls were passed in
        if (boClass == null) {
            throw new IllegalArgumentException("The Class passed in for the boClass argument was null.");
        }
        if (collectionName == null) {
            throw new IllegalArgumentException("The String passed in for the attributeName argument was null.");
        }

        PropertyDescriptor propertyDescriptor = null;

        // make an instance of the class passed
        Object classInstance;
        try {
            classInstance = boClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // make sure the attribute exists at all, throw exception if not
        try {
            propertyDescriptor = PropertyUtils.getPropertyDescriptor(classInstance, collectionName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (propertyDescriptor == null) {
            throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + collectionName
                    + "' does not exist " + "on class: '" + boClass.getName() + "'. GFK");
        }

        // get the class of the attribute name
        Class attributeClass = propertyDescriptor.getPropertyType();

        // make sure the class of the attribute descends from BusinessObject,
        // otherwise throw an exception
        if (!Collection.class.isAssignableFrom(attributeClass)) {
            throw new ObjectNotABusinessObjectRuntimeException(
                    "Attribute requested (" + collectionName + ") is of class: " + "'" + attributeClass.getName()
                            + "' and is not a " + "descendent of Collection");
        }

        // make sure the collection designated is listed as a
        // collection-descriptor
        // on the boClass specified, otherwise throw an exception
        ClassDescriptor classDescriptor = getClassDescriptor(boClass);
        CollectionDescriptor collectionDescriptor = classDescriptor.getCollectionDescriptorByName(collectionName);

        // in collections, the number of keys is equal to the number of keys in
        // the parent class (the class with the collection).
        // Each of the primary keys on the parent object will be mapped to a
        // field in the element object.

        List parentForeignKeys = getPrimaryKeys(boClass);
        Vector childPrimaryKeysLegacy = collectionDescriptor.getForeignKeyFields();

        if (parentForeignKeys.size() != childPrimaryKeysLegacy.size()) {
            throw new RuntimeException(
                    "The number of keys in the class descriptor and the inverse foreign key mapping for the collection descriptors do not match.");
        }

        Map<String, String> fkToPkMap = new HashMap<String, String>();

        Iterator pFKIter = parentForeignKeys.iterator();
        Iterator cPKIterator = childPrimaryKeysLegacy.iterator();

        while (pFKIter.hasNext()) {
            String parentForeignKey = (String) pFKIter.next();
            String childPrimaryKey = (String) cPKIterator.next();

            fkToPkMap.put(parentForeignKey, childPrimaryKey);
        }

        return fkToPkMap;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceService#getNestedForeignKeyMap(java.lang.Class)
     */

    @Override
    public Map getNestedForeignKeyMap(Class persistableObjectClass) {
        Map fkMap = new HashMap();
        ClassDescriptor classDescriptor = getClassDescriptor(persistableObjectClass);
        Vector objectReferences = classDescriptor.getObjectReferenceDescriptors();
        for (Iterator iter = objectReferences.iterator(); iter.hasNext();) {
            ObjectReferenceDescriptor objectReferenceDescriptor = (ObjectReferenceDescriptor) iter.next();
            ClassDescriptor referenceDescriptor = this.getClassDescriptor(objectReferenceDescriptor.getItemClass());

            FieldDescriptor[] fkFields = objectReferenceDescriptor.getForeignKeyFieldDescriptors(classDescriptor);
            FieldDescriptor[] pkFields = referenceDescriptor.getPkFields();
            for (int i = 0; i < pkFields.length; i++) {
                FieldDescriptor pkField = pkFields[i];
                fkMap.put(objectReferenceDescriptor.getAttributeName() + "." + pkField.getAttributeName(),
                        fkFields[i].getAttributeName());
            }
        }

        return fkMap;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceMetadataService#hasPrimaryKeyFieldValues(java.lang.Object)
     */
    @Override
    public boolean hasPrimaryKeyFieldValues(Object persistableObject) {
        Map keyFields = getPrimaryKeyFieldValues(persistableObject);

        boolean emptyField = false;
        for (Iterator i = keyFields.entrySet().iterator(); !emptyField && i.hasNext();) {
            Map.Entry e = (Map.Entry) i.next();

            Object fieldValue = e.getValue();
            if (fieldValue == null) {
                emptyField = true;
            } else if (fieldValue instanceof String) {
                if (StringUtils.isEmpty((String) fieldValue)) {
                    emptyField = true;
                } else {
                    emptyField = false;
                }
            }
        }

        return !emptyField;
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceService#getForeignKeyFieldsPopulationState(org.kuali.rice.krad.bo.BusinessObject,
     *      java.lang.String)
     */
    @Override
    public ForeignKeyFieldsPopulationState getForeignKeyFieldsPopulationState(PersistableBusinessObject bo,
            String referenceName) {
        boolean allFieldsPopulated = true;
        boolean anyFieldsPopulated = false;
        List<String> unpopulatedFields = new ArrayList<String>();

        // yelp if nulls were passed in
        if (bo == null) {
            throw new IllegalArgumentException("The Class passed in for the BusinessObject argument was null.");
        }
        if (StringUtils.isBlank(referenceName)) {
            throw new IllegalArgumentException(
                    "The String passed in for the referenceName argument was null or empty.");
        }

        PropertyDescriptor propertyDescriptor = null;

        // make sure the attribute exists at all, throw exception if not
        try {
            propertyDescriptor = PropertyUtils.getPropertyDescriptor(bo, referenceName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (propertyDescriptor == null) {
            throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + referenceName
                    + "' does not exist " + "on class: '" + bo.getClass().getName() + "'.");
        }

        // get the class of the attribute name
        Class referenceClass = propertyDescriptor.getPropertyType();

        // make sure the class of the attribute descends from BusinessObject,
        // otherwise throw an exception
        if (!PersistableBusinessObject.class.isAssignableFrom(referenceClass)) {
            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.");
        }

        // make sure the attribute designated is listed as a
        // reference-descriptor
        // on the clazz specified, otherwise throw an exception (OJB);

        ClassDescriptor classDescriptor = getClassDescriptor(bo.getClass());

        // This block is a combination of legacy and jpa
        ObjectReferenceDescriptor referenceDescriptor = classDescriptor
                .getObjectReferenceDescriptorByName(referenceName);
        if (referenceDescriptor == null) {
            throw new ReferenceAttributeNotAnOjbReferenceException(
                    "Attribute requested (" + referenceName + ") is not listed "
                            + "in OJB as a reference-descriptor for class: '" + bo.getClass().getName() + "'");
        }

        // get the list of the foreign-keys for this reference-descriptor
        Vector fkFieldsLegacy = referenceDescriptor.getForeignKeyFields();
        Iterator fkIteratorLegacy = fkFieldsLegacy.iterator();

        // walk through the list of the foreign keys, get their types
        while (fkIteratorLegacy.hasNext()) {

            // get the field name of the fk & pk field
            String fkFieldName = (String) fkIteratorLegacy.next();

            // get the value for the fk field
            Object fkFieldValue = null;
            try {
                fkFieldValue = PropertyUtils.getSimpleProperty(bo, fkFieldName);
            }

            // abort if the value is not retrievable
            catch (Exception e) {
                throw new RuntimeException(e);
            }

            // test the value
            if (fkFieldValue == null) {
                allFieldsPopulated = false;
                unpopulatedFields.add(fkFieldName);
            } else if (fkFieldValue instanceof String) {
                if (StringUtils.isBlank((String) fkFieldValue)) {
                    allFieldsPopulated = false;
                    unpopulatedFields.add(fkFieldName);
                } else {
                    anyFieldsPopulated = true;
                }
            } else {
                anyFieldsPopulated = true;
            }
        }

        // sanity check. if the flag for all fields populated is set, then
        // there should be nothing in the unpopulatedFields list
        if (allFieldsPopulated) {
            if (!unpopulatedFields.isEmpty()) {
                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.");
            }
        }

        return new ForeignKeyFieldsPopulationState(allFieldsPopulated, anyFieldsPopulated, unpopulatedFields);
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceStructureService#listReferenceObjectFieldNames(java.lang.Class)
     */

    @Override
    public Map<String, Class> listReferenceObjectFields(Class boClass) {
        // validate parameter
        if (boClass == null) {
            throw new IllegalArgumentException("Class specified in the parameter was null.");
        }

        Map<String, Class> references = new HashMap<String, Class>();
        ClassDescriptor classDescriptor = getClassDescriptor(boClass);
        Collection<ObjectReferenceDescriptor> referenceDescriptors = classDescriptor
                .getObjectReferenceDescriptors(true);

        for (ObjectReferenceDescriptor referenceDescriptor : referenceDescriptors) {
            /*
                * Below check is performed for OJB specific inheritance implementation. For more information see the OJB
                * documentation: http://db.apache.org/ojb/docu/guides/advanced-technique.html#table-per-subclass
                */
            String superReferenceDescriptor = referenceDescriptor.getAttributeName();
            if (!SuperReferenceDescriptor.SUPER_FIELD_INTERNAL_NAME.equals(superReferenceDescriptor)) {
                references.put(superReferenceDescriptor, referenceDescriptor.getItemClass());
            }
        }

        return references;
    }

    @Override
    public Map<String, Class> listCollectionObjectTypes(Class boClass) {
        if (boClass == null) {
            throw new IllegalArgumentException("Class specified in the parameter was null.");
        }

        Map<String, Class> references = new HashMap<String, Class>();
        ClassDescriptor classDescriptor = null;
        try {
            classDescriptor = getClassDescriptor(boClass);
        } catch (ClassNotPersistableException cnpe) {
            return references;
        }

        Collection<CollectionDescriptor> collectionDescriptors = classDescriptor.getCollectionDescriptors(true);
        for (CollectionDescriptor collectionDescriptor : collectionDescriptors) {
            references.put(collectionDescriptor.getAttributeName(), collectionDescriptor.getItemClass());
        }

        return references;
    }

    @Override
    public Map<String, Class> listCollectionObjectTypes(PersistableBusinessObject bo) {
        // validate parameter
        if (bo == null) {
            throw new IllegalArgumentException("BO specified in the parameter was null.");
        }
        if (!(bo instanceof PersistableBusinessObject)) {
            throw new IllegalArgumentException("BO specified [" + bo.getClass().getName()
                    + "] must be a class that " + "inherits from BusinessObject.");
        }

        return listCollectionObjectTypes(bo.getClass());
    }

    /**
     * @see org.kuali.rice.krad.service.PersistenceStructureService#listReferenceObjectFieldNames(org.kuali.rice.krad.bo.BusinessObject)
     */
    @Override
    public Map<String, Class> listReferenceObjectFields(PersistableBusinessObject bo) {
        // validate parameter
        if (bo == null) {
            throw new IllegalArgumentException("BO specified in the parameter was null.");
        }

        return listReferenceObjectFields(bo.getClass());
    }

    @Override
    public boolean isReferenceUpdatable(Class boClass, String referenceName) {
        ClassDescriptor classDescriptor = getClassDescriptor(boClass);
        ObjectReferenceDescriptor refDesc = classDescriptor.getObjectReferenceDescriptorByName(referenceName);
        return refDesc.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE;
    }

    @Override
    public boolean isCollectionUpdatable(Class boClass, String collectionName) {
        ClassDescriptor cd = getClassDescriptor(boClass);
        CollectionDescriptor collDesc = cd.getCollectionDescriptorByName(collectionName);
        return collDesc.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE;
    }

    @Override
    public boolean hasCollection(Class boClass, String collectionName) {
        ClassDescriptor cd = getClassDescriptor(boClass);
        return cd.getCollectionDescriptorByName(collectionName) != null;
    }

    @Override
    public boolean hasReference(Class boClass, String referenceName) {
        ClassDescriptor cd = getClassDescriptor(boClass);
        return cd.getObjectReferenceDescriptorByName(referenceName) != null;
    }

    /**
     * This overridden method ...
     *
     * @see org.kuali.rice.krad.service.PersistenceStructureService#getTableName(java.lang.Class)
     */

    @Override
    public String getTableName(Class<? extends PersistableBusinessObject> boClass) {
        ClassDescriptor cd = getClassDescriptor(boClass);
        return cd.getFullTableName();
    }

}