View Javadoc

1   /**
2    * Copyright 2005-2012 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.krad.bo.BusinessObject;
32  import org.kuali.rice.krad.bo.DataObjectRelationship;
33  import org.kuali.rice.krad.bo.PersistableBusinessObject;
34  import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
35  import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
36  import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
37  import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition;
38  import org.kuali.rice.krad.valuefinder.ValueFinder;
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.ObjectUtils;
45  
46  /**
47   * 
48   * Implementation of the <code>BusinessObjectMetaDataService</code> which uses
49   * the following services to gather its meta data:
50   * 
51   * @see BusinessObjectDictionaryService
52   * @see DataDictionaryService
53   * @see PersistenceStructureService
54   */
55  @Deprecated
56  public class BusinessObjectMetaDataServiceImpl extends DataObjectMetaDataServiceImpl implements BusinessObjectMetaDataService {
57  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
58  			.getLogger(BusinessObjectMetaDataServiceImpl.class);
59  
60  	private BusinessObjectDictionaryService businessObjectDictionaryService;
61  
62  	public Collection<String> getCollectionNames(BusinessObject bo) {
63  		return getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(bo.getClass().getName())
64  				.getCollectionNames();
65  	}
66  
67  	public Collection<String> getInquirableFieldNames(Class boClass, String sectionTitle) {
68  		return businessObjectDictionaryService.getInquiryFieldNames(boClass, sectionTitle);
69  	}
70  
71  	public List<String> getLookupableFieldNames(Class boClass) {
72  		return businessObjectDictionaryService.getLookupFieldNames(boClass);
73  	}
74  
75  	public String getLookupFieldDefaultValue(Class businessObjectClass, String attributeName) {
76  		return businessObjectDictionaryService.getLookupFieldDefaultValue(businessObjectClass, attributeName);
77  	}
78  
79  	public Class getLookupFieldDefaultValueFinderClass(Class businessObjectClass, String attributeName) {
80  		return businessObjectDictionaryService
81  				.getLookupFieldDefaultValueFinderClass(businessObjectClass, attributeName);
82  	}
83  
84  	/** {@inheritDoc} */
85  	public String getLookupFieldQuickfinderParameterString(Class businessObjectClass, String attributeName) {
86  		return businessObjectDictionaryService.getLookupFieldQuickfinderParameterString(businessObjectClass,
87  				attributeName);
88  	}
89  
90  	/** {@inheritDoc} */
91  	public Class<? extends ValueFinder> getLookupFieldQuickfinderParameterStringBuilderClass(Class businessObjectClass,
92  			String attributeName) {
93  		return businessObjectDictionaryService.getLookupFieldQuickfinderParameterStringBuilderClass(
94  				businessObjectClass, attributeName);
95  	}
96  
97  	public boolean isAttributeInquirable(Class boClass, String attributeName, String sectionTitle) {
98  		Collection sections = businessObjectDictionaryService.getInquirySections(boClass);
99  		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 }