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