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