View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kns.service.impl;
17  
18  import java.lang.reflect.InvocationTargetException;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.kuali.rice.kns.datadictionary.FieldDefinition;
28  import org.kuali.rice.kns.datadictionary.InquirySectionDefinition;
29  import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
30  import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
31  import org.kuali.rice.kns.service.KNSServiceLocator;
32  import org.kuali.rice.krad.bo.BusinessObject;
33  import org.kuali.rice.krad.bo.DataObjectRelationship;
34  import org.kuali.rice.krad.bo.PersistableBusinessObject;
35  import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
36  import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
37  import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
38  import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition;
39  import org.kuali.rice.krad.service.DataDictionaryService;
40  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
41  import org.kuali.rice.krad.service.ModuleService;
42  import org.kuali.rice.krad.service.PersistenceStructureService;
43  import org.kuali.rice.krad.service.impl.DataObjectMetaDataServiceImpl;
44  import org.kuali.rice.krad.util.LegacyDataFramework;
45  import org.kuali.rice.krad.util.ObjectUtils;
46  import org.kuali.rice.krad.valuefinder.ValueFinder;
47  
48  /**
49   *
50   * Implementation of the <code>BusinessObjectMetaDataService</code> which uses
51   * the following services to gather its meta data:
52   *
53   * @see BusinessObjectDictionaryService
54   * @see DataDictionaryService
55   * @see PersistenceStructureService
56   *
57   * @deprecated Only use {@link DataObjectMetaDataServiceImpl} if still using legacy data framework, otherwise use new
58   *             KRAD Data framework.
59   */
60  @Deprecated // Replaced by new metadata provider
61  @LegacyDataFramework
62  public class BusinessObjectMetaDataServiceImpl extends DataObjectMetaDataServiceImpl implements BusinessObjectMetaDataService {
63  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
64  			.getLogger(BusinessObjectMetaDataServiceImpl.class);
65  
66  	private BusinessObjectDictionaryService businessObjectDictionaryService;
67  
68  	@Override
69      public Collection<String> getCollectionNames(Object bo) {
70  		return getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(bo.getClass().getName())
71  				.getCollectionNames();
72  	}
73  
74  	@Override
75      public Collection<String> getInquirableFieldNames(Class boClass, String sectionTitle) {
76  		return businessObjectDictionaryService.getInquiryFieldNames(boClass, sectionTitle);
77  	}
78  
79  	@Override
80      public List<String> getLookupableFieldNames(Class boClass) {
81  		return businessObjectDictionaryService.getLookupFieldNames(boClass);
82  	}
83  
84  	@Override
85      public String getLookupFieldDefaultValue(Class businessObjectClass, String attributeName) {
86  		return businessObjectDictionaryService.getLookupFieldDefaultValue(businessObjectClass, attributeName);
87  	}
88  
89  	@Override
90      public Class getLookupFieldDefaultValueFinderClass(Class businessObjectClass, String attributeName) {
91  		return businessObjectDictionaryService
92  				.getLookupFieldDefaultValueFinderClass(businessObjectClass, attributeName);
93  	}
94  
95  	/** {@inheritDoc} */
96  	@Override
97      public String getLookupFieldQuickfinderParameterString(Class businessObjectClass, String attributeName) {
98  		return businessObjectDictionaryService.getLookupFieldQuickfinderParameterString(businessObjectClass,
99  				attributeName);
100 	}
101 
102 	/** {@inheritDoc} */
103 	@Override
104     public Class<? extends ValueFinder> getLookupFieldQuickfinderParameterStringBuilderClass(Class businessObjectClass,
105 			String attributeName) {
106 		return businessObjectDictionaryService.getLookupFieldQuickfinderParameterStringBuilderClass(
107 				businessObjectClass, attributeName);
108 	}
109 
110 	@Override
111     public boolean isAttributeInquirable(Class boClass, String attributeName, String sectionTitle) {
112 		Collection sections = businessObjectDictionaryService.getInquirySections(boClass);
113 		boolean isInquirable = true;
114 
115 		Iterator iter = sections.iterator();
116 
117 		while (iter.hasNext()) {
118 			InquirySectionDefinition def = (InquirySectionDefinition) iter.next();
119 			for (FieldDefinition field : def.getInquiryFields()) {
120 				if (field.getAttributeName().equalsIgnoreCase(attributeName)) {
121 					isInquirable = !field.isNoInquiry();
122 				}
123 			}
124 		}
125 		if (isInquirable) {
126 			Object obj = null;
127 			if (boClass != null && BusinessObject.class.isAssignableFrom(boClass)) {
128 				obj = ObjectUtils.createNewObjectFromClass(boClass);
129 			}
130 
131 			if (obj != null) {
132 				BusinessObject bo = (BusinessObject) obj;
133 				Class clazz = getNestedBOClass(bo, attributeName);
134 				if (clazz != null && BusinessObject.class.isAssignableFrom(clazz)) {
135 					return businessObjectDictionaryService.isInquirable(clazz);
136 				}
137 				else {
138 					return false;
139 				}
140 			}
141 			else {
142 				return false;
143 			}
144 
145 		}
146 
147 		return isInquirable;
148 	}
149 
150 	@Override
151     public boolean isInquirable(Class boClass) {
152 		boolean inquirable = false;
153 		ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(boClass);
154 		if (moduleService != null && moduleService.isExternalizable(boClass)) {
155 			inquirable = moduleService.isExternalizableBusinessObjectInquirable(boClass);
156 		}
157 		else {
158 			Boolean isLookupable = businessObjectDictionaryService.isInquirable(boClass);
159 			if (isLookupable != null) {
160 				inquirable = isLookupable.booleanValue();
161 			}
162 		}
163 		return inquirable;
164 	}
165 
166 	@Override
167     public boolean isAttributeLookupable(Class boClass, String attributeName) {
168 		Object obj = null;
169 		if (boClass != null && BusinessObject.class.isAssignableFrom(boClass)) {
170 			obj = ObjectUtils.createNewObjectFromClass(boClass);
171 		}
172 		if (obj != null) {
173 			BusinessObject bo = (BusinessObject) obj;
174 			DataObjectRelationship relationship = getBusinessObjectRelationship(bo, attributeName);
175 
176 			if (relationship != null && relationship.getRelatedClass() != null
177 					&& BusinessObject.class.isAssignableFrom(relationship.getRelatedClass())) {
178 				return isLookupable(relationship.getRelatedClass());
179 			}
180 			else {
181 				return false;
182 			}
183 		}
184 		else {
185 			return false;
186 		}
187 	}
188 
189 	@Override
190     public boolean isLookupable(Class boClass) {
191 		boolean lookupable = false;
192 		ModuleService moduleService =  getKualiModuleService().getResponsibleModuleService(boClass);
193 		if (moduleService != null && moduleService.isExternalizable(boClass)) {
194 			lookupable = moduleService.isExternalizableBusinessObjectLookupable(boClass);
195 		}
196 		else {
197 			Boolean isLookupable = businessObjectDictionaryService.isLookupable(boClass);
198 			if (isLookupable != null) {
199 				lookupable = isLookupable.booleanValue();
200 			}
201 		}
202 		return lookupable;
203 	}
204 
205 	@Override
206     public DataObjectRelationship getBusinessObjectRelationship(Object bo, String attributeName) {
207 		return getBusinessObjectRelationship(bo, bo.getClass(), attributeName, "", true);
208 	}
209 
210 	// TODO: four different exit points?!
211 	@Override
212     public DataObjectRelationship getBusinessObjectRelationship(RelationshipDefinition ddReference,
213             Object bo, Class boClass, String attributeName, String attributePrefix, boolean keysOnly) {
214 
215 		DataObjectRelationship relationship = null;
216 
217 		// if it is nested then replace the bo and attributeName with the
218 		// sub-refs
219 		if (ObjectUtils.isNestedAttribute(attributeName)) {
220 			if (ddReference != null) {
221 				relationship = new DataObjectRelationship(boClass, ddReference.getObjectAttributeName(),
222 						ddReference.getTargetClass());
223 				for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) {
224 					if (StringUtils.isNotBlank(attributePrefix)) {
225 						relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(),
226 								def.getTargetName());
227 					}
228 					else {
229 						relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
230 					}
231 				}
232 				if (!keysOnly) {
233 					for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) {
234 						if (StringUtils.isNotBlank(attributePrefix)) {
235 							relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(),
236 									def.getTargetName());
237 							if (def.isIdentifier()) {
238 								relationship.setUserVisibleIdentifierKey(attributePrefix + "." + def.getSourceName());
239 							}
240 						}
241 						else {
242 							relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
243 							if (def.isIdentifier()) {
244 								relationship.setUserVisibleIdentifierKey(def.getSourceName());
245 							}
246 						}
247 					}
248 				}
249 				return relationship;
250 			}
251 			// recurse down to the next object to find the relationship
252 
253 			String localPrefix = StringUtils.substringBefore(attributeName, ".");
254 			String localAttributeName = StringUtils.substringAfter(attributeName, ".");
255 			if (bo == null) {
256 				bo = (BusinessObject) ObjectUtils.createNewObjectFromClass(boClass);
257 			}
258 			Class nestedClass = ObjectUtils.getPropertyType(bo, localPrefix, getPersistenceStructureService());
259 			String fullPrefix = localPrefix;
260 			if (StringUtils.isNotBlank(attributePrefix)) {
261 				fullPrefix = attributePrefix + "." + localPrefix;
262 			}
263 			if (BusinessObject.class.isAssignableFrom(nestedClass)) {
264 				relationship = getBusinessObjectRelationship(null, nestedClass, localAttributeName, fullPrefix,
265 						keysOnly);
266 
267 				// Since it was a nested property, we need to set the "parent" object
268 				// back to the parent BO - the code above returns the next level down
269                 if (relationship != null) {
270 				    relationship.setParentClass(boClass);
271                 }
272 			}
273 			return relationship;
274 		}
275 		int maxSize = Integer.MAX_VALUE;
276 		// try persistable reference first
277 		if (PersistableBusinessObject.class.isAssignableFrom(boClass)
278 				&& getPersistenceStructureService().isPersistable(boClass)) {
279 			Map<String, DataObjectRelationship> rels = getPersistenceStructureService().getRelationshipMetadata(boClass,
280 					attributeName, attributePrefix);
281 			if (rels.size() > 0) {
282 				for (DataObjectRelationship rel : rels.values()) {
283 					if (rel.getParentToChildReferences().size() < maxSize && isLookupable(rel.getRelatedClass())) {
284 						maxSize = rel.getParentToChildReferences().size();
285 						relationship = rel;
286 					}
287 				}
288 			}
289 		}
290 		else {
291 			ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService()
292 					.getResponsibleModuleService(boClass);
293 			if (moduleService != null && moduleService.isExternalizable(boClass)) {
294 				relationship = getRelationshipMetadata(boClass, attributeName, attributePrefix);
295 				// relationship =
296 				// moduleService.getBusinessObjectRelationship(boClass,
297 				// attributeName, attributePrefix);
298 				if (relationship != null) {
299 					return relationship;
300 				}
301 			}
302 		}
303 
304 		// then check the DD for relationships defined there
305 		// TODO move out to a separate method
306 		// so that the logic for finding the relationships is similar to
307 		// primitiveReference
308 		if (ddReference != null && BusinessObject.class.isAssignableFrom(ddReference.getTargetClass()) && isLookupable(ddReference.getTargetClass()) && bo != null
309 				&& ddReference.getPrimitiveAttributes().size() < maxSize) {
310 			relationship = new DataObjectRelationship(boClass, ddReference.getObjectAttributeName(),
311 					ddReference.getTargetClass());
312 			for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) {
313 				relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
314 			}
315 			if (!keysOnly) {
316 				for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) {
317 					relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
318 				}
319 			}
320 		}
321 
322 		return relationship;
323 
324 	}
325 
326 
327 
328 	@Override
329     public RelationshipDefinition getBusinessObjectRelationshipDefinition(Class c, String attributeName) {
330 		return getDictionaryRelationship(c, attributeName);
331 	}
332 
333 	@Override
334     public RelationshipDefinition getBusinessObjectRelationshipDefinition(Object bo, String attributeName) {
335 		return getBusinessObjectRelationshipDefinition(bo.getClass(), attributeName);
336 	}
337 
338 	@Override
339     public DataObjectRelationship getBusinessObjectRelationship(Object bo, Class boClass,
340 			String attributeName, String attributePrefix, boolean keysOnly) {
341 		RelationshipDefinition ddReference = getBusinessObjectRelationshipDefinition(boClass, attributeName);
342 		return getBusinessObjectRelationship(ddReference, bo, boClass, attributeName, attributePrefix, keysOnly);
343 	}
344 
345 	/**
346 	 *
347 	 * This method retrieves the business object class for a specific attribute
348 	 *
349 	 * @param bo
350 	 * @param attributeName
351 	 * @return a business object class for a specific attribute
352 	 */
353 	private Class getNestedBOClass(BusinessObject bo, String attributeName) {
354 
355 		String[] nestedAttributes = StringUtils.split(attributeName, ".");
356 		String attributeRefName = "";
357 		Class clazz = null;
358 		if (nestedAttributes.length > 1) {
359 			String attributeStringSoFar = "";
360 			for (int i = 0; i < nestedAttributes.length - 1; i++) {
361 				try {
362 					// we need to build a string of the attribute names
363 					// depending on which iteration we're in.
364 					// so if the original attributeName string we're using is
365 					// "a.b.c.d.e", then first iteration would use
366 					// "a", 2nd "a.b", 3rd "a.b.c", etc.
367 					if (i != 0) {
368 						attributeStringSoFar = attributeStringSoFar + ".";
369 					}
370 					attributeStringSoFar = attributeStringSoFar + nestedAttributes[i];
371 					clazz = ObjectUtils.easyGetPropertyType(bo, attributeStringSoFar);
372 				}
373 				catch (InvocationTargetException ite) {
374 					LOG.info(ite);
375 					return null;
376 				}
377 				catch (NoSuchMethodException nsme) {
378 					LOG.info(nsme);
379 					return null;
380 				}
381 				catch (IllegalAccessException iae) {
382 					LOG.info(iae);
383 					return null;
384 				}
385 			}
386 		}
387 		return clazz;
388 	}
389 
390 	@Override
391     public List<DataObjectRelationship> getBusinessObjectRelationships(Object bo) {
392 		if (bo == null) {
393 			return null;
394 		}
395 		return getBusinessObjectRelationships(bo.getClass());
396 	}
397 
398 	@Override
399     @SuppressWarnings("unchecked")
400 	public List<DataObjectRelationship> getBusinessObjectRelationships(Class<? extends Object> boClass) {
401 		if (boClass == null) {
402 			return null;
403 		}
404 
405 		Map<String, Class> referenceClasses = null;
406 		if (PersistableBusinessObject.class.isAssignableFrom(boClass)
407 				&& getPersistenceStructureService().isPersistable(boClass)) {
408 			referenceClasses = getPersistenceStructureService().listReferenceObjectFields(boClass);
409 		}
410 		DataDictionaryEntry ddEntry = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(
411 				boClass.getName());
412 		List<RelationshipDefinition> ddRelationships = (ddEntry == null ? new ArrayList<RelationshipDefinition>()
413 				: ddEntry.getRelationships());
414 		List<DataObjectRelationship> relationships = new ArrayList<DataObjectRelationship>();
415 
416 		// loop over all relationships
417 		if (referenceClasses != null) {
418 			for (Map.Entry<String, Class> entry : referenceClasses.entrySet()) {
419 				if (isLookupable(entry.getValue())) {
420 					Map<String, String> fkToPkRefs = getPersistenceStructureService().getForeignKeysForReference(boClass,
421 							entry.getKey());
422 					DataObjectRelationship rel = new DataObjectRelationship(boClass, entry.getKey(),
423 							entry.getValue());
424 					for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) {
425 						rel.getParentToChildReferences().put(ref.getKey(), ref.getValue());
426 					}
427 					relationships.add(rel);
428 				}
429 			}
430 		}
431 
432 		for (RelationshipDefinition rd : ddRelationships) {
433 			if (isLookupable(rd.getTargetClass())) {
434 				DataObjectRelationship rel = new DataObjectRelationship(boClass, rd.getObjectAttributeName(),
435 						rd.getTargetClass());
436 				for (PrimitiveAttributeDefinition def : rd.getPrimitiveAttributes()) {
437 					rel.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
438 				}
439 				relationships.add(rel);
440 			}
441 		}
442 
443 		return relationships;
444 	}
445 
446 	/***************************************************************************
447 	 * @see org.kuali.rice.kns.service.BusinessObjectMetaDataService#getReferencesForForeignKey(java.lang.Class,
448 	 *      java.lang.String)
449 	 */
450 	@Override
451     public Map<String, Class> getReferencesForForeignKey(Object bo, String attributeName) {
452 		List<DataObjectRelationship> dataObjectRelationships = getBusinessObjectRelationships(bo);
453 		Map<String, Class> referencesForForeignKey = new HashMap<String, Class>();
454 		for (DataObjectRelationship dataObjectRelationship : dataObjectRelationships) {
455 			if (dataObjectRelationship != null && dataObjectRelationship.getParentToChildReferences() != null
456 					&& dataObjectRelationship.getParentToChildReferences().containsKey(attributeName)) {
457 				referencesForForeignKey.put(dataObjectRelationship.getParentAttributeName(),
458 						dataObjectRelationship.getRelatedClass());
459 			}
460 		}
461 		return referencesForForeignKey;
462 	}
463 
464 	@Override
465     public String getForeignKeyFieldName(Class businessObjectClass, String attributeName, String targetName) {
466 
467 		String fkName = "";
468 
469 		// first try DD-based relationships
470 		RelationshipDefinition relationshipDefinition = getDictionaryRelationship(businessObjectClass, attributeName);
471 
472 		if (relationshipDefinition != null) {
473 			List<PrimitiveAttributeDefinition> primitives = relationshipDefinition.getPrimitiveAttributes();
474 			for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitives) {
475 				if (primitiveAttributeDefinition.getTargetName().equals(targetName)) {
476 					fkName = primitiveAttributeDefinition.getSourceName();
477 					break;
478 				}
479 			}
480 		}
481 
482 		// if we can't find anything in the DD, then try the persistence service
483 		if (StringUtils.isBlank(fkName) && PersistableBusinessObject.class.isAssignableFrom(businessObjectClass)
484 				&& getPersistenceStructureService().isPersistable(businessObjectClass)) {
485 			fkName = getPersistenceStructureService().getForeignKeyFieldName(businessObjectClass, attributeName,
486 					targetName);
487 		}
488 		return fkName;
489 	}
490 
491     /**
492      * @see org.kuali.rice.krad.service.DataObjectMetaDataService#hasLocalLookup
493      */
494     @Override
495     public boolean hasLocalLookup(Class<?> dataObjectClass) {
496         boolean hasLookup = super.hasLocalLookup(dataObjectClass);
497 
498         // if no krad lookup check for kns
499         if (!hasLookup) {
500             Boolean isLookupable = getBusinessObjectDictionaryService().isLookupable(dataObjectClass);
501             if (isLookupable != null) {
502                 hasLookup = isLookupable.booleanValue();
503             }
504         }
505 
506         return hasLookup;
507     }
508 
509     /**
510      * @see org.kuali.rice.krad.service.DataObjectMetaDataService#hasLocalInquiry
511      */
512     @Override
513     public boolean hasLocalInquiry(Class<?> dataObjectClass) {
514         boolean hasInquiry = super.hasLocalInquiry(dataObjectClass);
515 
516         // if no krad inquiry check for kns
517         if (!hasInquiry) {
518             Boolean isInquirable = getBusinessObjectDictionaryService().isInquirable(dataObjectClass);
519             if (isInquirable != null) {
520                 hasInquiry = isInquirable.booleanValue();
521             }
522         }
523 
524         return hasInquiry;
525     }
526 
527     /**
528      * Gets the businessObjectDictionaryService attribute.
529      *
530      * @return Returns the businessObjectDictionaryService.
531      */
532     protected BusinessObjectDictionaryService getBusinessObjectDictionaryService() {
533         if (businessObjectDictionaryService == null) {
534             businessObjectDictionaryService = KNSServiceLocator.getBusinessObjectDictionaryService();
535         }
536 
537         return businessObjectDictionaryService;
538     }
539 
540    	/**
541    	 * Sets the businessObjectDictionaryService attribute value.
542    	 *
543    	 * @param businessObjectDictionaryService
544    	 *            The BusinessObjectDictionaryService to set.
545    	 */
546    	public void setBusinessObjectDictionaryService(BusinessObjectDictionaryService businessObjectDictionaryService) {
547    		this.businessObjectDictionaryService = businessObjectDictionaryService;
548    	}
549 }