001    /**
002     * Copyright 2005-2012 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     */
016    package org.kuali.rice.kns.service.impl;
017    
018    import java.lang.reflect.InvocationTargetException;
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    
026    import org.apache.commons.lang.StringUtils;
027    import org.kuali.rice.kns.datadictionary.FieldDefinition;
028    import org.kuali.rice.kns.datadictionary.InquirySectionDefinition;
029    import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
030    import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
031    import org.kuali.rice.krad.bo.BusinessObject;
032    import org.kuali.rice.krad.bo.DataObjectRelationship;
033    import org.kuali.rice.krad.bo.PersistableBusinessObject;
034    import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
035    import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
036    import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
037    import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition;
038    import org.kuali.rice.krad.valuefinder.ValueFinder;
039    import org.kuali.rice.krad.service.DataDictionaryService;
040    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
041    import org.kuali.rice.krad.service.ModuleService;
042    import org.kuali.rice.krad.service.PersistenceStructureService;
043    import org.kuali.rice.krad.service.impl.DataObjectMetaDataServiceImpl;
044    import 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
056    public 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    }