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.kns.service.impl;
017
018import java.lang.reflect.InvocationTargetException;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.commons.lang.StringUtils;
027import org.kuali.rice.kns.datadictionary.FieldDefinition;
028import org.kuali.rice.kns.datadictionary.InquirySectionDefinition;
029import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
030import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
031import org.kuali.rice.krad.bo.BusinessObject;
032import org.kuali.rice.krad.bo.DataObjectRelationship;
033import org.kuali.rice.krad.bo.PersistableBusinessObject;
034import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
035import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
036import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
037import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition;
038import org.kuali.rice.krad.valuefinder.ValueFinder;
039import org.kuali.rice.krad.service.DataDictionaryService;
040import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
041import org.kuali.rice.krad.service.ModuleService;
042import org.kuali.rice.krad.service.PersistenceStructureService;
043import org.kuali.rice.krad.service.impl.DataObjectMetaDataServiceImpl;
044import org.kuali.rice.krad.util.ObjectUtils;
045
046/**
047 * 
048 * Implementation of the <code>BusinessObjectMetaDataService</code> which uses
049 * the following services to gather its meta data:
050 * 
051 * @see BusinessObjectDictionaryService
052 * @see DataDictionaryService
053 * @see PersistenceStructureService
054 */
055@Deprecated
056public class BusinessObjectMetaDataServiceImpl extends DataObjectMetaDataServiceImpl implements BusinessObjectMetaDataService {
057        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
058                        .getLogger(BusinessObjectMetaDataServiceImpl.class);
059
060        private BusinessObjectDictionaryService businessObjectDictionaryService;
061
062        public Collection<String> getCollectionNames(BusinessObject bo) {
063                return getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(bo.getClass().getName())
064                                .getCollectionNames();
065        }
066
067        public Collection<String> getInquirableFieldNames(Class boClass, String sectionTitle) {
068                return businessObjectDictionaryService.getInquiryFieldNames(boClass, sectionTitle);
069        }
070
071        public List<String> getLookupableFieldNames(Class boClass) {
072                return businessObjectDictionaryService.getLookupFieldNames(boClass);
073        }
074
075        public String getLookupFieldDefaultValue(Class businessObjectClass, String attributeName) {
076                return businessObjectDictionaryService.getLookupFieldDefaultValue(businessObjectClass, attributeName);
077        }
078
079        public Class getLookupFieldDefaultValueFinderClass(Class businessObjectClass, String attributeName) {
080                return businessObjectDictionaryService
081                                .getLookupFieldDefaultValueFinderClass(businessObjectClass, attributeName);
082        }
083
084        /** {@inheritDoc} */
085        public String getLookupFieldQuickfinderParameterString(Class businessObjectClass, String attributeName) {
086                return businessObjectDictionaryService.getLookupFieldQuickfinderParameterString(businessObjectClass,
087                                attributeName);
088        }
089
090        /** {@inheritDoc} */
091        public Class<? extends ValueFinder> getLookupFieldQuickfinderParameterStringBuilderClass(Class businessObjectClass,
092                        String attributeName) {
093                return businessObjectDictionaryService.getLookupFieldQuickfinderParameterStringBuilderClass(
094                                businessObjectClass, attributeName);
095        }
096
097        public boolean isAttributeInquirable(Class boClass, String attributeName, String sectionTitle) {
098                Collection sections = businessObjectDictionaryService.getInquirySections(boClass);
099                boolean isInquirable = true;
100
101                Iterator iter = sections.iterator();
102
103                while (iter.hasNext()) {
104                        InquirySectionDefinition def = (InquirySectionDefinition) iter.next();
105                        for (FieldDefinition field : def.getInquiryFields()) {
106                                if (field.getAttributeName().equalsIgnoreCase(attributeName)) {
107                                        isInquirable = !field.isNoInquiry();
108                                }
109                        }
110                }
111                if (isInquirable) {
112                        Object obj = null;
113                        if (boClass != null && BusinessObject.class.isAssignableFrom(boClass)) {
114                                obj = ObjectUtils.createNewObjectFromClass(boClass);
115                        }
116
117                        if (obj != null) {
118                                BusinessObject bo = (BusinessObject) obj;
119                                Class clazz = getNestedBOClass(bo, attributeName);
120                                if (clazz != null && BusinessObject.class.isAssignableFrom(clazz)) {
121                                        return businessObjectDictionaryService.isInquirable(clazz);
122                                }
123                                else {
124                                        return false;
125                                }
126                        }
127                        else {
128                                return false;
129                        }
130
131                }
132
133                return isInquirable;
134        }
135
136        public boolean isInquirable(Class boClass) {
137                boolean inquirable = false;
138                ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(boClass);
139                if (moduleService != null && moduleService.isExternalizable(boClass)) {
140                        inquirable = moduleService.isExternalizableBusinessObjectInquirable(boClass);
141                }
142                else {
143                        Boolean isLookupable = businessObjectDictionaryService.isInquirable(boClass);
144                        if (isLookupable != null) {
145                                inquirable = isLookupable.booleanValue();
146                        }
147                }
148                return inquirable;
149        }
150
151        public boolean isAttributeLookupable(Class boClass, String attributeName) {
152                Object obj = null;
153                if (boClass != null && BusinessObject.class.isAssignableFrom(boClass)) {
154                        obj = ObjectUtils.createNewObjectFromClass(boClass);
155                }
156                if (obj != null) {
157                        BusinessObject bo = (BusinessObject) obj;
158                        DataObjectRelationship relationship = getBusinessObjectRelationship(bo, attributeName);
159
160                        if (relationship != null && relationship.getRelatedClass() != null
161                                        && BusinessObject.class.isAssignableFrom(relationship.getRelatedClass())) {
162                                return isLookupable(relationship.getRelatedClass());
163                        }
164                        else {
165                                return false;
166                        }
167                }
168                else {
169                        return false;
170                }
171        }
172
173        public boolean isLookupable(Class boClass) {
174                boolean lookupable = false;
175                ModuleService moduleService =  getKualiModuleService().getResponsibleModuleService(boClass);
176                if (moduleService != null && moduleService.isExternalizable(boClass)) {
177                        lookupable = moduleService.isExternalizableBusinessObjectLookupable(boClass);
178                }
179                else {
180                        Boolean isLookupable = businessObjectDictionaryService.isLookupable(boClass);
181                        if (isLookupable != null) {
182                                lookupable = isLookupable.booleanValue();
183                        }
184                }
185                return lookupable;
186        }
187
188        public DataObjectRelationship getBusinessObjectRelationship(BusinessObject bo, String attributeName) {
189                return getBusinessObjectRelationship(bo, bo.getClass(), attributeName, "", true);
190        }
191
192        // TODO: four different exit points?!
193        public DataObjectRelationship getBusinessObjectRelationship(RelationshipDefinition ddReference,
194                        BusinessObject bo, Class boClass, String attributeName, String attributePrefix, boolean keysOnly) {
195
196                DataObjectRelationship relationship = null;
197
198                // if it is nested then replace the bo and attributeName with the
199                // sub-refs
200                if (ObjectUtils.isNestedAttribute(attributeName)) {
201                        if (ddReference != null) {
202                                relationship = new DataObjectRelationship(boClass, ddReference.getObjectAttributeName(),
203                                                ddReference.getTargetClass());
204                                for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) {
205                                        if (StringUtils.isNotBlank(attributePrefix)) {
206                                                relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(),
207                                                                def.getTargetName());
208                                        }
209                                        else {
210                                                relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
211                                        }
212                                }
213                                if (!keysOnly) {
214                                        for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) {
215                                                if (StringUtils.isNotBlank(attributePrefix)) {
216                                                        relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(),
217                                                                        def.getTargetName());
218                                                        if (def.isIdentifier()) {
219                                                                relationship.setUserVisibleIdentifierKey(attributePrefix + "." + def.getSourceName());
220                                                        }
221                                                }
222                                                else {
223                                                        relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
224                                                        if (def.isIdentifier()) {
225                                                                relationship.setUserVisibleIdentifierKey(def.getSourceName());
226                                                        }
227                                                }
228                                        }
229                                }
230                                return relationship;
231                        }
232                        // recurse down to the next object to find the relationship
233
234                        String localPrefix = StringUtils.substringBefore(attributeName, ".");
235                        String localAttributeName = StringUtils.substringAfter(attributeName, ".");
236                        if (bo == null) {
237                                bo = (BusinessObject) ObjectUtils.createNewObjectFromClass(boClass);
238                        }
239                        Class nestedClass = ObjectUtils.getPropertyType(bo, localPrefix, getPersistenceStructureService());
240                        String fullPrefix = localPrefix;
241                        if (StringUtils.isNotBlank(attributePrefix)) {
242                                fullPrefix = attributePrefix + "." + localPrefix;
243                        }
244                        if (BusinessObject.class.isAssignableFrom(nestedClass)) {
245                                relationship = getBusinessObjectRelationship(null, nestedClass, localAttributeName, fullPrefix,
246                                                keysOnly);
247                        }
248                        return relationship;
249                }
250                int maxSize = Integer.MAX_VALUE;
251                // try persistable reference first
252                if (PersistableBusinessObject.class.isAssignableFrom(boClass)
253                                && getPersistenceStructureService().isPersistable(boClass)) {
254                        Map<String, DataObjectRelationship> rels = getPersistenceStructureService().getRelationshipMetadata(boClass,
255                                        attributeName, attributePrefix);
256                        if (rels.size() > 0) {
257                                for (DataObjectRelationship rel : rels.values()) {
258                                        if (rel.getParentToChildReferences().size() < maxSize && isLookupable(rel.getRelatedClass())) {
259                                                maxSize = rel.getParentToChildReferences().size();
260                                                relationship = rel;
261                                        }
262                                }
263                        }
264                }
265                else {
266                        ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService()
267                                        .getResponsibleModuleService(boClass);
268                        if (moduleService != null && moduleService.isExternalizable(boClass)) {
269                                relationship = getRelationshipMetadata(boClass, attributeName, attributePrefix);
270                                // relationship =
271                                // moduleService.getBusinessObjectRelationship(boClass,
272                                // attributeName, attributePrefix);
273                                if (relationship != null) {
274                                        return relationship;
275                                }
276                        }
277                }
278
279                // then check the DD for relationships defined there
280                // TODO move out to a separate method
281                // so that the logic for finding the relationships is similar to
282                // primitiveReference
283                if (ddReference != null && isLookupable(ddReference.getTargetClass()) && bo != null
284                                && ddReference.getPrimitiveAttributes().size() < maxSize) {
285                        relationship = new DataObjectRelationship(boClass, ddReference.getObjectAttributeName(),
286                                        ddReference.getTargetClass());
287                        for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) {
288                                relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
289                        }
290                        if (!keysOnly) {
291                                for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) {
292                                        relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
293                                }
294                        }
295                }
296
297                return relationship;
298
299        }
300
301
302
303        public RelationshipDefinition getBusinessObjectRelationshipDefinition(Class c, String attributeName) {
304                return getDictionaryRelationship(c, attributeName);
305        }
306
307        public RelationshipDefinition getBusinessObjectRelationshipDefinition(BusinessObject bo, String attributeName) {
308                return getBusinessObjectRelationshipDefinition(bo.getClass(), attributeName);
309        }
310
311        public DataObjectRelationship getBusinessObjectRelationship(BusinessObject bo, Class boClass,
312                        String attributeName, String attributePrefix, boolean keysOnly) {
313                RelationshipDefinition ddReference = getBusinessObjectRelationshipDefinition(boClass, attributeName);
314                return getBusinessObjectRelationship(ddReference, bo, boClass, attributeName, attributePrefix, keysOnly);
315        }
316
317        /**
318         * Gets the businessObjectDictionaryService attribute.
319         * 
320         * @return Returns the businessObjectDictionaryService.
321         */
322        public BusinessObjectDictionaryService getBusinessObjectDictionaryService() {
323                return businessObjectDictionaryService;
324        }
325
326        /**
327         * Sets the businessObjectDictionaryService attribute value.
328         * 
329         * @param businessObjectDictionaryService
330         *            The BusinessObjectDictionaryService to set.
331         */
332        public void setBusinessObjectDictionaryService(BusinessObjectDictionaryService businessObjectDictionaryService) {
333                this.businessObjectDictionaryService = businessObjectDictionaryService;
334        }
335
336        /**
337         * 
338         * This method retrieves the business object class for a specific attribute
339         * 
340         * @param bo
341         * @param attributeName
342         * @return a business object class for a specific attribute
343         */
344        private Class getNestedBOClass(BusinessObject bo, String attributeName) {
345
346                String[] nestedAttributes = StringUtils.split(attributeName, ".");
347                String attributeRefName = "";
348                Class clazz = null;
349                if (nestedAttributes.length > 1) {
350                        String attributeStringSoFar = "";
351                        for (int i = 0; i < nestedAttributes.length - 1; i++) {
352                                try {
353                                        // we need to build a string of the attribute names
354                                        // depending on which iteration we're in.
355                                        // so if the original attributeName string we're using is
356                                        // "a.b.c.d.e", then first iteration would use
357                                        // "a", 2nd "a.b", 3rd "a.b.c", etc.
358                                        if (i != 0) {
359                                                attributeStringSoFar = attributeStringSoFar + ".";
360                                        }
361                                        attributeStringSoFar = attributeStringSoFar + nestedAttributes[i];
362                                        clazz = ObjectUtils.easyGetPropertyType(bo, attributeStringSoFar);
363                                }
364                                catch (InvocationTargetException ite) {
365                                        LOG.info(ite);
366                                        return null;
367                                }
368                                catch (NoSuchMethodException nsme) {
369                                        LOG.info(nsme);
370                                        return null;
371                                }
372                                catch (IllegalAccessException iae) {
373                                        LOG.info(iae);
374                                        return null;
375                                }
376                        }
377                }
378                return clazz;
379        }
380
381        public List<DataObjectRelationship> getBusinessObjectRelationships(BusinessObject bo) {
382                if (bo == null) {
383                        return null;
384                }
385                return getBusinessObjectRelationships(bo.getClass());
386        }
387
388        @SuppressWarnings("unchecked")
389        public List<DataObjectRelationship> getBusinessObjectRelationships(Class<? extends BusinessObject> boClass) {
390                if (boClass == null) {
391                        return null;
392                }
393
394                Map<String, Class> referenceClasses = null;
395                if (PersistableBusinessObject.class.isAssignableFrom(boClass)
396                                && getPersistenceStructureService().isPersistable(boClass)) {
397                        referenceClasses = getPersistenceStructureService().listReferenceObjectFields(boClass);
398                }
399                DataDictionaryEntry ddEntry = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(
400                                boClass.getName());
401                List<RelationshipDefinition> ddRelationships = (ddEntry == null ? new ArrayList<RelationshipDefinition>()
402                                : ddEntry.getRelationships());
403                List<DataObjectRelationship> relationships = new ArrayList<DataObjectRelationship>();
404
405                // loop over all relationships
406                if (referenceClasses != null) {
407                        for (Map.Entry<String, Class> entry : referenceClasses.entrySet()) {
408                                if (isLookupable(entry.getValue())) {
409                                        Map<String, String> fkToPkRefs = getPersistenceStructureService().getForeignKeysForReference(boClass,
410                                                        entry.getKey());
411                                        DataObjectRelationship rel = new DataObjectRelationship(boClass, entry.getKey(),
412                                                        entry.getValue());
413                                        for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) {
414                                                rel.getParentToChildReferences().put(ref.getKey(), ref.getValue());
415                                        }
416                                        relationships.add(rel);
417                                }
418                        }
419                }
420
421                for (RelationshipDefinition rd : ddRelationships) {
422                        if (isLookupable(rd.getTargetClass())) {
423                                DataObjectRelationship rel = new DataObjectRelationship(boClass, rd.getObjectAttributeName(),
424                                                rd.getTargetClass());
425                                for (PrimitiveAttributeDefinition def : rd.getPrimitiveAttributes()) {
426                                        rel.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
427                                }
428                                relationships.add(rel);
429                        }
430                }
431
432                return relationships;
433        }
434
435        /***************************************************************************
436         * @see org.kuali.rice.kns.service.BusinessObjectMetaDataService#getReferencesForForeignKey(java.lang.Class,
437         *      java.lang.String)
438         */
439        public Map<String, Class> getReferencesForForeignKey(BusinessObject bo, String attributeName) {
440                List<DataObjectRelationship> dataObjectRelationships = getBusinessObjectRelationships(bo);
441                Map<String, Class> referencesForForeignKey = new HashMap<String, Class>();
442                for (DataObjectRelationship dataObjectRelationship : dataObjectRelationships) {
443                        if (dataObjectRelationship != null && dataObjectRelationship.getParentToChildReferences() != null
444                                        && dataObjectRelationship.getParentToChildReferences().containsKey(attributeName)) {
445                                referencesForForeignKey.put(dataObjectRelationship.getParentAttributeName(),
446                                                dataObjectRelationship.getRelatedClass());
447                        }
448                }
449                return referencesForForeignKey;
450        }
451
452        public String getForeignKeyFieldName(Class businessObjectClass, String attributeName, String targetName) {
453
454                String fkName = "";
455
456                // first try DD-based relationships
457                RelationshipDefinition relationshipDefinition = getDictionaryRelationship(businessObjectClass, attributeName);
458
459                if (relationshipDefinition != null) {
460                        List<PrimitiveAttributeDefinition> primitives = relationshipDefinition.getPrimitiveAttributes();
461                        for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitives) {
462                                if (primitiveAttributeDefinition.getTargetName().equals(targetName)) {
463                                        fkName = primitiveAttributeDefinition.getSourceName();
464                                        break;
465                                }
466                        }
467                }
468
469                // if we can't find anything in the DD, then try the persistence service
470                if (StringUtils.isBlank(fkName) && PersistableBusinessObject.class.isAssignableFrom(businessObjectClass)
471                                && getPersistenceStructureService().isPersistable(businessObjectClass)) {
472                        fkName = getPersistenceStructureService().getForeignKeyFieldName(businessObjectClass, attributeName,
473                                        targetName);
474                }
475                return fkName;
476        }
477}