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}